aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/includes')
-rw-r--r--phpBB/includes/acp/acp_forums.php6
-rw-r--r--phpBB/includes/acp/acp_groups.php2
-rw-r--r--phpBB/includes/acp/acp_main.php18
-rw-r--r--phpBB/includes/acp/acp_modules.php6
-rw-r--r--phpBB/includes/acp/acp_permissions.php27
-rw-r--r--phpBB/includes/acp/acp_ranks.php14
-rw-r--r--phpBB/includes/acp/acp_styles.php4
-rw-r--r--phpBB/includes/acp/acp_users.php4
-rw-r--r--phpBB/includes/acp/info/acp_extensions.php4
-rw-r--r--phpBB/includes/acp/info/acp_language.php2
-rw-r--r--phpBB/includes/auth/auth.php22
-rw-r--r--phpBB/includes/bbcode.php2
-rw-r--r--phpBB/includes/cache/driver/apc.php2
-rw-r--r--phpBB/includes/cache/driver/eaccelerator.php4
-rw-r--r--phpBB/includes/cache/driver/file.php33
-rw-r--r--phpBB/includes/cache/driver/interface.php50
-rw-r--r--phpBB/includes/cache/driver/memcache.php4
-rw-r--r--phpBB/includes/cache/driver/memory.php17
-rw-r--r--phpBB/includes/cache/driver/null.php5
-rw-r--r--phpBB/includes/cache/driver/redis.php32
-rw-r--r--phpBB/includes/cache/driver/wincache.php2
-rw-r--r--phpBB/includes/cache/driver/xcache.php2
-rw-r--r--phpBB/includes/cache/service.php133
-rw-r--r--phpBB/includes/captcha/captcha_non_gd.php2
-rw-r--r--phpBB/includes/config/config.php2
-rw-r--r--phpBB/includes/config/db.php8
-rw-r--r--phpBB/includes/controller/exception.php24
-rw-r--r--phpBB/includes/controller/helper.php117
-rw-r--r--phpBB/includes/controller/provider.php82
-rw-r--r--phpBB/includes/controller/resolver.php128
-rw-r--r--phpBB/includes/cron/manager.php2
-rw-r--r--phpBB/includes/cron/task/core/prune_all_forums.php6
-rw-r--r--phpBB/includes/cron/task/core/prune_forum.php8
-rw-r--r--phpBB/includes/cron/task/core/queue.php2
-rw-r--r--phpBB/includes/cron/task/core/tidy_cache.php2
-rw-r--r--phpBB/includes/cron/task/core/tidy_database.php2
-rw-r--r--phpBB/includes/cron/task/core/tidy_search.php6
-rw-r--r--phpBB/includes/cron/task/core/tidy_sessions.php2
-rw-r--r--phpBB/includes/cron/task/core/tidy_warnings.php2
-rw-r--r--phpBB/includes/cron/task/parametrized.php2
-rw-r--r--phpBB/includes/cron/task/provider.php59
-rw-r--r--phpBB/includes/cron/task/task.php2
-rw-r--r--phpBB/includes/db/db_tools.php18
-rw-r--r--phpBB/includes/db/driver/driver.php (renamed from phpBB/includes/db/dbal.php)25
-rw-r--r--phpBB/includes/db/driver/firebird.php (renamed from phpBB/includes/db/firebird.php)14
-rw-r--r--phpBB/includes/db/driver/mssql.php (renamed from phpBB/includes/db/mssql.php)82
-rw-r--r--phpBB/includes/db/driver/mssql_odbc.php (renamed from phpBB/includes/db/mssql_odbc.php)54
-rw-r--r--phpBB/includes/db/driver/mssqlnative.php (renamed from phpBB/includes/db/mssqlnative.php)60
-rw-r--r--phpBB/includes/db/driver/mysql.php (renamed from phpBB/includes/db/mysql.php)61
-rw-r--r--phpBB/includes/db/driver/mysqli.php (renamed from phpBB/includes/db/mysqli.php)48
-rw-r--r--phpBB/includes/db/driver/oracle.php (renamed from phpBB/includes/db/oracle.php)69
-rw-r--r--phpBB/includes/db/driver/postgres.php (renamed from phpBB/includes/db/postgres.php)21
-rw-r--r--phpBB/includes/db/driver/sqlite.php (renamed from phpBB/includes/db/sqlite.php)57
-rw-r--r--phpBB/includes/di/extension/config.php84
-rw-r--r--phpBB/includes/di/extension/core.php69
-rw-r--r--phpBB/includes/di/extension/ext.php69
-rw-r--r--phpBB/includes/di/pass/collection_pass.php46
-rw-r--r--phpBB/includes/di/pass/kernel_pass.php68
-rw-r--r--phpBB/includes/di/processor/config.php76
-rw-r--r--phpBB/includes/di/processor/ext.php54
-rw-r--r--phpBB/includes/di/processor/interface.php28
-rw-r--r--phpBB/includes/di/service_collection.php49
-rw-r--r--phpBB/includes/event/dispatcher.php4
-rw-r--r--phpBB/includes/event/kernel_exception_subscriber.php85
-rw-r--r--phpBB/includes/event/kernel_request_subscriber.php83
-rw-r--r--phpBB/includes/event/kernel_terminate_subscriber.php43
-rw-r--r--phpBB/includes/extension/controller.php84
-rw-r--r--phpBB/includes/extension/controller_interface.php31
-rw-r--r--phpBB/includes/extension/interface.php4
-rw-r--r--phpBB/includes/extension/manager.php16
-rw-r--r--phpBB/includes/extension/metadata_manager.php4
-rw-r--r--phpBB/includes/functions.php268
-rw-r--r--phpBB/includes/functions_acp.php20
-rw-r--r--phpBB/includes/functions_admin.php56
-rw-r--r--phpBB/includes/functions_container.php199
-rw-r--r--phpBB/includes/functions_database_helper.php206
-rw-r--r--phpBB/includes/functions_display.php28
-rw-r--r--phpBB/includes/functions_download.php10
-rw-r--r--phpBB/includes/functions_install.php66
-rw-r--r--phpBB/includes/functions_messenger.php89
-rw-r--r--phpBB/includes/functions_module.php21
-rw-r--r--phpBB/includes/functions_posting.php5
-rw-r--r--phpBB/includes/functions_upload.php15
-rw-r--r--phpBB/includes/functions_url_matcher.php106
-rw-r--r--phpBB/includes/functions_user.php111
-rw-r--r--phpBB/includes/group_positions.php10
-rw-r--r--phpBB/includes/hook/finder.php84
-rw-r--r--phpBB/includes/lock/db.php8
-rw-r--r--phpBB/includes/lock/flock.php133
-rw-r--r--phpBB/includes/mcp/mcp_forum.php15
-rw-r--r--phpBB/includes/mcp/mcp_main.php22
-rw-r--r--phpBB/includes/mcp/mcp_pm_reports.php1
-rw-r--r--phpBB/includes/mcp/mcp_reports.php21
-rw-r--r--phpBB/includes/mcp/mcp_topic.php56
-rw-r--r--phpBB/includes/message_parser.php11
-rw-r--r--phpBB/includes/questionnaire/questionnaire.php4
-rw-r--r--phpBB/includes/search/fulltext_mysql.php14
-rw-r--r--phpBB/includes/search/fulltext_native.php4
-rw-r--r--phpBB/includes/search/fulltext_postgres.php65
-rw-r--r--phpBB/includes/search/fulltext_sphinx.php6
-rw-r--r--phpBB/includes/sphinxapi.php3424
-rw-r--r--phpBB/includes/style/resource_locator.php118
-rw-r--r--phpBB/includes/style/style.php37
-rw-r--r--phpBB/includes/template/compile.php23
-rw-r--r--phpBB/includes/template/filter.php141
-rw-r--r--phpBB/includes/template/locator.php56
-rw-r--r--phpBB/includes/template/template.php106
-rw-r--r--phpBB/includes/ucp/info/ucp_profile.php2
-rw-r--r--phpBB/includes/ucp/ucp_groups.php8
-rw-r--r--phpBB/includes/ucp/ucp_pm_viewmessage.php1
-rw-r--r--phpBB/includes/ucp/ucp_prefs.php33
-rw-r--r--phpBB/includes/ucp/ucp_profile.php9
112 files changed, 5094 insertions, 2777 deletions
diff --git a/phpBB/includes/acp/acp_forums.php b/phpBB/includes/acp/acp_forums.php
index c6dbf5eb9c..7e8d5d8388 100644
--- a/phpBB/includes/acp/acp_forums.php
+++ b/phpBB/includes/acp/acp_forums.php
@@ -206,7 +206,7 @@ class acp_forums
($action != 'edit' || empty($forum_id) || ($auth->acl_get('a_fauth') && $auth->acl_get('a_authusers') && $auth->acl_get('a_authgroups') && $auth->acl_get('a_mauth'))))
{
copy_forum_permissions($forum_perm_from, $forum_data['forum_id'], ($action == 'edit') ? true : false);
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
$copied_permissions = true;
}
/* Commented out because of questionable UI workflow - re-visit for 3.0.7
@@ -266,7 +266,7 @@ class acp_forums
add_log('admin', 'LOG_FORUM_' . strtoupper($action), $row['forum_name'], $move_forum_name);
$cache->destroy('sql', FORUMS_TABLE);
}
-
+
if ($request->is_ajax())
{
$json_response = new phpbb_json_response;
@@ -768,7 +768,7 @@ class acp_forums
if (!empty($forum_perm_from) && $forum_perm_from != $forum_id)
{
copy_forum_permissions($forum_perm_from, $forum_id, true);
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
$auth->acl_clear_prefetch();
$cache->destroy('sql', FORUMS_TABLE);
diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php
index b604e20094..9145a20400 100644
--- a/phpBB/includes/acp/acp_groups.php
+++ b/phpBB/includes/acp/acp_groups.php
@@ -439,7 +439,7 @@ class acp_groups
foreach ($test_variables as $test => $type)
{
- if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || in_array($test, $set_attributes)))
+ if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || isset($group_attributes['group_avatar']) && strpos($test, 'avatar') === 0 || in_array($test, $set_attributes)))
{
settype($submit_ary[$test], $type);
$group_attributes['group_' . $test] = $group_row['group_' . $test] = $submit_ary[$test];
diff --git a/phpBB/includes/acp/acp_main.php b/phpBB/includes/acp/acp_main.php
index d419bc3b99..c44bc1b8a6 100644
--- a/phpBB/includes/acp/acp_main.php
+++ b/phpBB/includes/acp/acp_main.php
@@ -24,7 +24,7 @@ class acp_main
function main($id, $mode)
{
- global $config, $db, $user, $auth, $template, $request;
+ global $config, $db, $cache, $user, $auth, $template, $request;
global $phpbb_root_path, $phpbb_admin_path, $phpEx;
// Show restore permissions notice
@@ -129,7 +129,7 @@ class acp_main
set_config('record_online_users', 1, true);
set_config('record_online_date', time(), true);
add_log('admin', 'LOG_RESET_ONLINE');
-
+
if ($request->is_ajax())
{
trigger_error('RESET_ONLINE_SUCCESS');
@@ -184,7 +184,7 @@ class acp_main
update_last_username();
add_log('admin', 'LOG_RESYNC_STATS');
-
+
if ($request->is_ajax())
{
trigger_error('RESYNC_STATS_SUCCESS');
@@ -251,7 +251,7 @@ class acp_main
}
add_log('admin', 'LOG_RESYNC_POSTCOUNTS');
-
+
if ($request->is_ajax())
{
trigger_error('RESYNC_POSTCOUNTS_SUCCESS');
@@ -266,7 +266,7 @@ class acp_main
set_config('board_startdate', time() - 1);
add_log('admin', 'LOG_RESET_DATE');
-
+
if ($request->is_ajax())
{
trigger_error('RESET_DATE_SUCCESS');
@@ -346,7 +346,7 @@ class acp_main
}
add_log('admin', 'LOG_RESYNC_POST_MARKING');
-
+
if ($request->is_ajax())
{
trigger_error('RESYNC_POST_MARKING_SUCCESS');
@@ -359,10 +359,10 @@ class acp_main
// Clear permissions
$auth->acl_clear_prefetch();
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
add_log('admin', 'LOG_PURGE_CACHE');
-
+
if ($request->is_ajax())
{
trigger_error('PURGE_CACHE_SUCCESS');
@@ -413,7 +413,7 @@ class acp_main
$db->sql_query($sql);
add_log('admin', 'LOG_PURGE_SESSIONS');
-
+
if ($request->is_ajax())
{
trigger_error('PURGE_SESSIONS_SUCCESS');
diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php
index 8528dc91c4..52a82004e8 100644
--- a/phpBB/includes/acp/acp_modules.php
+++ b/phpBB/includes/acp/acp_modules.php
@@ -740,15 +740,15 @@ class acp_modules
*/
function remove_cache_file()
{
- global $cache;
+ global $phpbb_container;
// Sanitise for future path use, it's escaped as appropriate for queries
$p_class = str_replace(array('.', '/', '\\'), '', basename($this->module_class));
- $cache->destroy('_modules_' . $p_class);
+ $phpbb_container->get('cache.driver')->destroy('_modules_' . $p_class);
// Additionally remove sql cache
- $cache->destroy('sql', MODULES_TABLE);
+ $phpbb_container->get('cache.driver')->destroy('sql', MODULES_TABLE);
}
/**
diff --git a/phpBB/includes/acp/acp_permissions.php b/phpBB/includes/acp/acp_permissions.php
index dd071074de..a64765f4f5 100644
--- a/phpBB/includes/acp/acp_permissions.php
+++ b/phpBB/includes/acp/acp_permissions.php
@@ -656,7 +656,7 @@ class acp_permissions
*/
function set_permissions($mode, $permission_type, &$auth_admin, &$user_id, &$group_id)
{
- global $user, $auth;
+ global $db, $cache, $user, $auth;
global $request;
$psubmit = request_var('psubmit', array(0 => array(0 => 0)));
@@ -726,13 +726,13 @@ class acp_permissions
// Do we need to recache the moderator lists?
if ($permission_type == 'm_')
{
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
}
// Remove users who are now moderators or admins from everyones foes list
if ($permission_type == 'm_' || $permission_type == 'a_')
{
- update_foes($group_id, $user_id);
+ phpbb_update_foes($db, $auth, $group_id, $user_id);
}
$this->log_action($mode, 'add', $permission_type, $ug_type, $ug_id, $forum_id);
@@ -745,7 +745,7 @@ class acp_permissions
*/
function set_all_permissions($mode, $permission_type, &$auth_admin, &$user_id, &$group_id)
{
- global $user, $auth;
+ global $db, $cache, $user, $auth;
global $request;
// User or group to be set?
@@ -794,13 +794,13 @@ class acp_permissions
// Do we need to recache the moderator lists?
if ($permission_type == 'm_')
{
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
}
// Remove users who are now moderators or admins from everyones foes list
if ($permission_type == 'm_' || $permission_type == 'a_')
{
- update_foes($group_id, $user_id);
+ phpbb_update_foes($db, $auth, $group_id, $user_id);
}
$this->log_action($mode, 'add', $permission_type, $ug_type, $ug_ids, $forum_ids);
@@ -858,7 +858,7 @@ class acp_permissions
*/
function remove_permissions($mode, $permission_type, &$auth_admin, &$user_id, &$group_id, &$forum_id)
{
- global $user, $db, $auth;
+ global $user, $db, $cache, $auth;
// User or group to be set?
$ug_type = (sizeof($user_id)) ? 'user' : 'group';
@@ -874,7 +874,7 @@ class acp_permissions
// Do we need to recache the moderator lists?
if ($permission_type == 'm_')
{
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
}
$this->log_action($mode, 'del', $permission_type, $ug_type, (($ug_type == 'user') ? $user_id : $group_id), (sizeof($forum_id) ? $forum_id : array(0 => 0)));
@@ -952,12 +952,7 @@ class acp_permissions
if ($user_id != $user->data['user_id'])
{
- $sql = 'SELECT user_id, username, user_permissions, user_type
- FROM ' . USERS_TABLE . '
- WHERE user_id = ' . $user_id;
- $result = $db->sql_query($sql);
- $userdata = $db->sql_fetchrow($result);
- $db->sql_freeresult($result);
+ $userdata = $auth->obtain_user_data($user_id);
}
else
{
@@ -1172,7 +1167,7 @@ class acp_permissions
*/
function copy_forum_permissions()
{
- global $auth, $cache, $template, $user;
+ global $db, $auth, $cache, $template, $user;
$user->add_lang('acp/forums');
@@ -1187,7 +1182,7 @@ class acp_permissions
{
if (copy_forum_permissions($src, $dest))
{
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
$auth->acl_clear_prefetch();
$cache->destroy('sql', FORUMS_TABLE);
diff --git a/phpBB/includes/acp/acp_ranks.php b/phpBB/includes/acp/acp_ranks.php
index d9ed5b17f1..6b06d03f52 100644
--- a/phpBB/includes/acp/acp_ranks.php
+++ b/phpBB/includes/acp/acp_ranks.php
@@ -71,7 +71,7 @@ class acp_ranks
'rank_min' => $min_posts,
'rank_image' => htmlspecialchars_decode($rank_image)
);
-
+
if ($rank_id)
{
$sql = 'UPDATE ' . RANKS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . " WHERE rank_id = $rank_id";
@@ -122,7 +122,7 @@ class acp_ranks
$cache->destroy('_ranks');
add_log('admin', 'LOG_RANK_REMOVED', $rank_title);
-
+
if ($request->is_ajax())
{
$json_response = new phpbb_json_response;
@@ -151,7 +151,7 @@ class acp_ranks
case 'add':
$data = $ranks = $existing_imgs = array();
-
+
$sql = 'SELECT *
FROM ' . RANKS_TABLE . '
ORDER BY rank_min ASC, rank_special ASC';
@@ -209,17 +209,17 @@ class acp_ranks
'RANK_TITLE' => (isset($ranks['rank_title'])) ? $ranks['rank_title'] : '',
'S_FILENAME_LIST' => $filename_list,
- 'RANK_IMAGE' => ($edit_img) ? $phpbb_root_path . $config['ranks_path'] . '/' . $edit_img : $phpbb_admin_path . 'images/spacer.gif',
+ 'RANK_IMAGE' => ($edit_img) ? $phpbb_root_path . $config['ranks_path'] . '/' . $edit_img : htmlspecialchars($phpbb_admin_path) . 'images/spacer.gif',
'S_SPECIAL_RANK' => (isset($ranks['rank_special']) && $ranks['rank_special']) ? true : false,
'MIN_POSTS' => (isset($ranks['rank_min']) && !$ranks['rank_special']) ? $ranks['rank_min'] : 0)
);
-
+
return;
break;
}
-
+
$template->assign_vars(array(
'U_ACTION' => $this->u_action)
);
@@ -241,7 +241,7 @@ class acp_ranks
'U_EDIT' => $this->u_action . '&action=edit&id=' . $row['rank_id'],
'U_DELETE' => $this->u_action . '&action=delete&id=' . $row['rank_id'])
- );
+ );
}
$db->sql_freeresult($result);
diff --git a/phpBB/includes/acp/acp_styles.php b/phpBB/includes/acp/acp_styles.php
index db77825ae7..266495972b 100644
--- a/phpBB/includes/acp/acp_styles.php
+++ b/phpBB/includes/acp/acp_styles.php
@@ -137,11 +137,13 @@ class acp_styles
*/
protected function action_cache()
{
+ global $db, $cache, $auth;
+
$this->cache->purge();
// Clear permissions
$this->auth->acl_clear_prefetch();
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
add_log('admin', 'LOG_PURGE_CACHE');
diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php
index 82d8ef5cbb..2bdbf1441a 100644
--- a/phpBB/includes/acp/acp_users.php
+++ b/phpBB/includes/acp/acp_users.php
@@ -1352,7 +1352,6 @@ class acp_users
$data = array(
'icq' => request_var('icq', $user_row['user_icq']),
'aim' => request_var('aim', $user_row['user_aim']),
- 'msn' => request_var('msn', $user_row['user_msnm']),
'yim' => request_var('yim', $user_row['user_yim']),
'jabber' => utf8_normalize_nfc(request_var('jabber', $user_row['user_jabber'], true)),
'website' => request_var('website', $user_row['user_website']),
@@ -1382,7 +1381,6 @@ class acp_users
array('string', true, 3, 15),
array('match', true, '#^[0-9]+$#i')),
'aim' => array('string', true, 3, 255),
- 'msn' => array('string', true, 5, 255),
'jabber' => array(
array('string', true, 5, 255),
array('jabber')),
@@ -1416,7 +1414,6 @@ class acp_users
$sql_ary = array(
'user_icq' => $data['icq'],
'user_aim' => $data['aim'],
- 'user_msnm' => $data['msn'],
'user_yim' => $data['yim'],
'user_jabber' => $data['jabber'],
'user_website' => $data['website'],
@@ -1469,7 +1466,6 @@ class acp_users
'ICQ' => $data['icq'],
'YIM' => $data['yim'],
'AIM' => $data['aim'],
- 'MSN' => $data['msn'],
'JABBER' => $data['jabber'],
'WEBSITE' => $data['website'],
'LOCATION' => $data['location'],
diff --git a/phpBB/includes/acp/info/acp_extensions.php b/phpBB/includes/acp/info/acp_extensions.php
index f5953fb1dd..03d7059165 100644
--- a/phpBB/includes/acp/info/acp_extensions.php
+++ b/phpBB/includes/acp/info/acp_extensions.php
@@ -16,10 +16,10 @@ class acp_extensions_info
{
return array(
'filename' => 'acp_extensions',
- 'title' => 'ACP_EXTENSIONS',
+ 'title' => 'ACP_EXTENSIONS_MANAGEMENT',
'version' => '1.0.0',
'modes' => array(
- 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_GENERAL_TASKS')),
+ 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSIONS_MANAGEMENT')),
),
);
}
diff --git a/phpBB/includes/acp/info/acp_language.php b/phpBB/includes/acp/info/acp_language.php
index 85dfb119ea..7f33a22fa6 100644
--- a/phpBB/includes/acp/info/acp_language.php
+++ b/phpBB/includes/acp/info/acp_language.php
@@ -19,7 +19,7 @@ class acp_language_info
'title' => 'ACP_LANGUAGE',
'version' => '1.0.0',
'modes' => array(
- 'lang_packs' => array('title' => 'ACP_LANGUAGE_PACKS', 'auth' => 'acl_a_language', 'cat' => array('ACP_GENERAL_TASKS')),
+ 'lang_packs' => array('title' => 'ACP_LANGUAGE_PACKS', 'auth' => 'acl_a_language', 'cat' => array('ACP_LANGUAGE')),
),
);
}
diff --git a/phpBB/includes/auth/auth.php b/phpBB/includes/auth/auth.php
index e3bccaf47b..2535247571 100644
--- a/phpBB/includes/auth/auth.php
+++ b/phpBB/includes/auth/auth.php
@@ -103,6 +103,26 @@ class phpbb_auth
}
/**
+ * Retrieves data wanted by acl function from the database for the
+ * specified user.
+ *
+ * @param int $user_id User ID
+ * @return array User attributes
+ */
+ public function obtain_user_data($user_id)
+ {
+ global $db;
+
+ $sql = 'SELECT user_id, username, user_permissions, user_type
+ FROM ' . USERS_TABLE . '
+ WHERE user_id = ' . $user_id;
+ $result = $db->sql_query($sql);
+ $user_data = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+ return $user_data;
+ }
+
+ /**
* Fill ACL array with relevant bitstrings from user_permissions column
* @access private
*/
@@ -191,7 +211,7 @@ class phpbb_auth
/**
* Get forums with the specified permission setting
- * if the option is prefixed with !, then the result becomes nagated
+ * if the option is prefixed with !, then the result becomes negated
*
* @param bool $clean set to true if only values needs to be returned which are set/unset
*/
diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php
index b9ffa8091c..e8681420d4 100644
--- a/phpBB/includes/bbcode.php
+++ b/phpBB/includes/bbcode.php
@@ -134,7 +134,7 @@ class bbcode
$style_resource_locator = new phpbb_style_resource_locator();
$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider());
- $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context());
+ $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager);
$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $template);
$style->set_style();
$template->set_filenames(array('bbcode.html' => 'bbcode.html'));
diff --git a/phpBB/includes/cache/driver/apc.php b/phpBB/includes/cache/driver/apc.php
index dc0144fac3..0516b669c8 100644
--- a/phpBB/includes/cache/driver/apc.php
+++ b/phpBB/includes/cache/driver/apc.php
@@ -26,7 +26,7 @@ class phpbb_cache_driver_apc extends phpbb_cache_driver_memory
/**
* Purge cache data
*
- * @return void
+ * @return null
*/
function purge()
{
diff --git a/phpBB/includes/cache/driver/eaccelerator.php b/phpBB/includes/cache/driver/eaccelerator.php
index 7939f043c9..257b90c76e 100644
--- a/phpBB/includes/cache/driver/eaccelerator.php
+++ b/phpBB/includes/cache/driver/eaccelerator.php
@@ -30,7 +30,7 @@ class phpbb_cache_driver_eaccelerator extends phpbb_cache_driver_memory
/**
* Purge cache data
*
- * @return void
+ * @return null
*/
function purge()
{
@@ -47,7 +47,7 @@ class phpbb_cache_driver_eaccelerator extends phpbb_cache_driver_memory
/**
* Perform cache garbage collection
*
- * @return void
+ * @return null
*/
function tidy()
{
diff --git a/phpBB/includes/cache/driver/file.php b/phpBB/includes/cache/driver/file.php
index f64a9e3ea8..85decbe3e8 100644
--- a/phpBB/includes/cache/driver/file.php
+++ b/phpBB/includes/cache/driver/file.php
@@ -214,7 +214,12 @@ class phpbb_cache_driver_file extends phpbb_cache_driver_base
while (($entry = readdir($dir)) !== false)
{
- if (strpos($entry, 'sql_') !== 0 && strpos($entry, 'data_') !== 0 && strpos($entry, 'ctpl_') !== 0 && strpos($entry, 'tpl_') !== 0)
+ if (strpos($entry, 'container_') !== 0 &&
+ strpos($entry, 'url_matcher') !== 0 &&
+ strpos($entry, 'sql_') !== 0 &&
+ strpos($entry, 'data_') !== 0 &&
+ strpos($entry, 'ctpl_') !== 0 &&
+ strpos($entry, 'tpl_') !== 0)
{
continue;
}
@@ -362,12 +367,10 @@ class phpbb_cache_driver_file extends phpbb_cache_driver_base
}
/**
- * Save sql query
+ * {@inheritDoc}
*/
- function sql_save($query, $query_result, $ttl)
+ function sql_save(phpbb_db_driver $db, $query, $query_result, $ttl)
{
- global $db;
-
// Remove extra spaces and tabs
$query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
@@ -383,10 +386,10 @@ class phpbb_cache_driver_file extends phpbb_cache_driver_base
if ($this->_write('sql_' . md5($query), $this->sql_rowset[$query_id], $ttl + time(), $query))
{
- $query_result = $query_id;
+ return $query_id;
}
- return $query_id;
+ return $query_result;
}
/**
@@ -648,10 +651,11 @@ class phpbb_cache_driver_file extends phpbb_cache_driver_base
$file = "{$this->cache_dir}$filename.$phpEx";
+ $lock = new phpbb_lock_flock($file);
+ $lock->acquire();
+
if ($handle = @fopen($file, 'wb'))
{
- @flock($handle, LOCK_EX);
-
// File header
fwrite($handle, '<' . '?php exit; ?' . '>');
@@ -692,7 +696,6 @@ class phpbb_cache_driver_file extends phpbb_cache_driver_base
fwrite($handle, $data);
}
- @flock($handle, LOCK_UN);
fclose($handle);
if (!function_exists('phpbb_chmod'))
@@ -703,10 +706,16 @@ class phpbb_cache_driver_file extends phpbb_cache_driver_base
phpbb_chmod($file, CHMOD_READ | CHMOD_WRITE);
- return true;
+ $return_value = true;
+ }
+ else
+ {
+ $return_value = false;
}
- return false;
+ $lock->release();
+
+ return $return_value;
}
/**
diff --git a/phpBB/includes/cache/driver/interface.php b/phpBB/includes/cache/driver/interface.php
index 847ba97262..53f684d1c8 100644
--- a/phpBB/includes/cache/driver/interface.php
+++ b/phpBB/includes/cache/driver/interface.php
@@ -63,42 +63,82 @@ interface phpbb_cache_driver_interface
public function destroy($var_name, $table = '');
/**
- * Check if a given cache entry exist
+ * Check if a given cache entry exists
*/
public function _exists($var_name);
/**
- * Load cached sql query
+ * Load result of an SQL query from cache.
+ *
+ * @param string $query SQL query
+ *
+ * @return int|bool Query ID (integer) if cache contains a rowset
+ * for the specified query.
+ * False otherwise.
*/
public function sql_load($query);
/**
- * Save sql query
+ * Save result of an SQL query in cache.
+ *
+ * In persistent cache stores, this function stores the query
+ * result to persistent storage. In other words, there is no need
+ * to call save() afterwards.
+ *
+ * @param phpbb_db_driver $db Database connection
+ * @param string $query SQL query, should be used for generating storage key
+ * @param mixed $query_result The result from dbal::sql_query, to be passed to
+ * dbal::sql_fetchrow to get all rows and store them
+ * in cache.
+ * @param int $ttl Time to live, after this timeout the query should
+ * expire from the cache.
+ * @return int|mixed If storing in cache succeeded, an integer $query_id
+ * representing the query should be returned. Otherwise
+ * the original $query_result should be returned.
*/
- public function sql_save($query, $query_result, $ttl);
+ public function sql_save(phpbb_db_driver $db, $query, $query_result, $ttl);
/**
- * Ceck if a given sql query exist in cache
+ * Check if result for a given SQL query exists in cache.
+ *
+ * @param int $query_id
+ * @return bool
*/
public function sql_exists($query_id);
/**
* Fetch row from cache (database)
+ *
+ * @param int $query_id
+ * @return array|bool The query result if found in the cache, otherwise
+ * false.
*/
public function sql_fetchrow($query_id);
/**
* Fetch a field from the current row of a cached database result (database)
+ *
+ * @param int $query_id
+ * @param $field The name of the column.
+ * @return string|bool The field of the query result if found in the cache,
+ * otherwise false.
*/
public function sql_fetchfield($query_id, $field);
/**
* Seek a specific row in an a cached database result (database)
+ *
+ * @param int $rownum Row to seek to.
+ * @param int $query_id
+ * @return bool
*/
public function sql_rowseek($rownum, $query_id);
/**
* Free memory used for a cached database result (database)
+ *
+ * @param int $query_id
+ * @return bool
*/
public function sql_freeresult($query_id);
}
diff --git a/phpBB/includes/cache/driver/memcache.php b/phpBB/includes/cache/driver/memcache.php
index 9fe70f8b44..3fd16b23b0 100644
--- a/phpBB/includes/cache/driver/memcache.php
+++ b/phpBB/includes/cache/driver/memcache.php
@@ -64,7 +64,7 @@ class phpbb_cache_driver_memcache extends phpbb_cache_driver_memory
/**
* Unload the cache resources
*
- * @return void
+ * @return null
*/
function unload()
{
@@ -76,7 +76,7 @@ class phpbb_cache_driver_memcache extends phpbb_cache_driver_memory
/**
* Purge cache data
*
- * @return void
+ * @return null
*/
function purge()
{
diff --git a/phpBB/includes/cache/driver/memory.php b/phpBB/includes/cache/driver/memory.php
index e0771ab1d3..f77a1df316 100644
--- a/phpBB/includes/cache/driver/memory.php
+++ b/phpBB/includes/cache/driver/memory.php
@@ -162,7 +162,12 @@ abstract class phpbb_cache_driver_memory extends phpbb_cache_driver_base
while (($entry = readdir($dir)) !== false)
{
- if (strpos($entry, 'sql_') !== 0 && strpos($entry, 'data_') !== 0 && strpos($entry, 'ctpl_') !== 0 && strpos($entry, 'tpl_') !== 0)
+ if (strpos($entry, 'container_') !== 0 &&
+ strpos($entry, 'url_matcher') !== 0 &&
+ strpos($entry, 'sql_') !== 0 &&
+ strpos($entry, 'data_') !== 0 &&
+ strpos($entry, 'ctpl_') !== 0 &&
+ strpos($entry, 'tpl_') !== 0)
{
continue;
}
@@ -278,12 +283,10 @@ abstract class phpbb_cache_driver_memory extends phpbb_cache_driver_base
}
/**
- * Save sql query
+ * {@inheritDoc}
*/
- function sql_save($query, $query_result, $ttl)
+ function sql_save(phpbb_db_driver $db, $query, $query_result, $ttl)
{
- global $db;
-
// Remove extra spaces and tabs
$query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
$hash = md5($query);
@@ -294,7 +297,7 @@ abstract class phpbb_cache_driver_memory extends phpbb_cache_driver_base
if (!preg_match('/FROM \\(?(`?\\w+`?(?: \\w+)?(?:, ?`?\\w+`?(?: \\w+)?)*)\\)?/', $query, $regs))
{
// Bail out if the match fails.
- return;
+ return $query_result;
}
$tables = array_map('trim', explode(',', $regs[1]));
@@ -334,8 +337,6 @@ abstract class phpbb_cache_driver_memory extends phpbb_cache_driver_base
$this->_write('sql_' . $hash, $this->sql_rowset[$query_id], $ttl);
- $query_result = $query_id;
-
return $query_id;
}
diff --git a/phpBB/includes/cache/driver/null.php b/phpBB/includes/cache/driver/null.php
index df2c6c026f..2fadc27ba3 100644
--- a/phpBB/includes/cache/driver/null.php
+++ b/phpBB/includes/cache/driver/null.php
@@ -105,10 +105,11 @@ class phpbb_cache_driver_null extends phpbb_cache_driver_base
}
/**
- * Save sql query
+ * {@inheritDoc}
*/
- function sql_save($query, $query_result, $ttl)
+ function sql_save(phpbb_db_driver $db, $query, $query_result, $ttl)
{
+ return $query_result;
}
/**
diff --git a/phpBB/includes/cache/driver/redis.php b/phpBB/includes/cache/driver/redis.php
index a768885962..960735b673 100644
--- a/phpBB/includes/cache/driver/redis.php
+++ b/phpBB/includes/cache/driver/redis.php
@@ -39,13 +39,39 @@ class phpbb_cache_driver_redis extends phpbb_cache_driver_memory
var $redis;
+ /**
+ * Creates a redis cache driver.
+ *
+ * The following global constants affect operation:
+ *
+ * PHPBB_ACM_REDIS_HOST
+ * PHPBB_ACM_REDIS_PORT
+ * PHPBB_ACM_REDIS_PASSWORD
+ * PHPBB_ACM_REDIS_DB
+ *
+ * There are no publicly documented constructor parameters.
+ */
function __construct()
{
// Call the parent constructor
parent::__construct();
$this->redis = new Redis();
- $this->redis->connect(PHPBB_ACM_REDIS_HOST, PHPBB_ACM_REDIS_PORT);
+
+ $args = func_get_args();
+ if (!empty($args))
+ {
+ $ok = call_user_func_array(array($this->redis, 'connect'), $args);
+ }
+ else
+ {
+ $ok = $this->redis->connect(PHPBB_ACM_REDIS_HOST, PHPBB_ACM_REDIS_PORT);
+ }
+
+ if (!$ok)
+ {
+ trigger_error('Could not connect to redis server');
+ }
if (defined('PHPBB_ACM_REDIS_PASSWORD'))
{
@@ -74,7 +100,7 @@ class phpbb_cache_driver_redis extends phpbb_cache_driver_memory
/**
* Unload the cache resources
*
- * @return void
+ * @return null
*/
function unload()
{
@@ -86,7 +112,7 @@ class phpbb_cache_driver_redis extends phpbb_cache_driver_memory
/**
* Purge cache data
*
- * @return void
+ * @return null
*/
function purge()
{
diff --git a/phpBB/includes/cache/driver/wincache.php b/phpBB/includes/cache/driver/wincache.php
index fa9eb95f88..58f3b4a581 100644
--- a/phpBB/includes/cache/driver/wincache.php
+++ b/phpBB/includes/cache/driver/wincache.php
@@ -26,7 +26,7 @@ class phpbb_cache_driver_wincache extends phpbb_cache_driver_memory
/**
* Purge cache data
*
- * @return void
+ * @return null
*/
function purge()
{
diff --git a/phpBB/includes/cache/driver/xcache.php b/phpBB/includes/cache/driver/xcache.php
index 0b768bdb3e..06c5fafd97 100644
--- a/phpBB/includes/cache/driver/xcache.php
+++ b/phpBB/includes/cache/driver/xcache.php
@@ -41,7 +41,7 @@ class phpbb_cache_driver_xcache extends phpbb_cache_driver_memory
/**
* Purge cache data
*
- * @return void
+ * @return null
*/
function purge()
{
diff --git a/phpBB/includes/cache/service.php b/phpBB/includes/cache/service.php
index e63ec6e33a..69c5e0fdd0 100644
--- a/phpBB/includes/cache/service.php
+++ b/phpBB/includes/cache/service.php
@@ -21,16 +21,57 @@ if (!defined('IN_PHPBB'))
*/
class phpbb_cache_service
{
- private $driver;
+ /**
+ * Cache driver.
+ *
+ * @var phpbb_cache_driver_interface
+ */
+ protected $driver;
+
+ /**
+ * The config.
+ *
+ * @var phpbb_config
+ */
+ protected $config;
+
+ /**
+ * Database connection.
+ *
+ * @var phpbb_db_driver
+ */
+ protected $db;
+
+ /**
+ * Root path.
+ *
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * PHP extension.
+ *
+ * @var string
+ */
+ protected $php_ext;
/**
* Creates a cache service around a cache driver
*
* @param phpbb_cache_driver_interface $driver The cache driver
+ * @param phpbb_config $config The config
+ * @param phpbb_db_driver $db Database connection
+ * @param string $phpbb_root_path Root path
+ * @param string $php_ext PHP extension
*/
- public function __construct(phpbb_cache_driver_interface $driver = null)
+ public function __construct(phpbb_cache_driver_interface $driver, phpbb_config $config, phpbb_db_driver $db, $phpbb_root_path, $php_ext)
{
$this->set_driver($driver);
+ $this->config = $config;
+ $this->db = $db;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
}
/**
@@ -64,21 +105,19 @@ class phpbb_cache_service
*/
function obtain_word_list()
{
- global $db;
-
if (($censors = $this->driver->get('_word_censors')) === false)
{
$sql = 'SELECT word, replacement
FROM ' . WORDS_TABLE;
- $result = $db->sql_query($sql);
+ $result = $this->db->sql_query($sql);
$censors = array();
- while ($row = $db->sql_fetchrow($result))
+ while ($row = $this->db->sql_fetchrow($result))
{
$censors['match'][] = get_censor_preg_expression($row['word']);
$censors['replace'][] = $row['replacement'];
}
- $db->sql_freeresult($result);
+ $this->db->sql_freeresult($result);
$this->driver->put('_word_censors', $censors);
}
@@ -93,23 +132,21 @@ class phpbb_cache_service
{
if (($icons = $this->driver->get('_icons')) === false)
{
- global $db;
-
// Topic icons
$sql = 'SELECT *
FROM ' . ICONS_TABLE . '
ORDER BY icons_order';
- $result = $db->sql_query($sql);
+ $result = $this->db->sql_query($sql);
$icons = array();
- while ($row = $db->sql_fetchrow($result))
+ while ($row = $this->db->sql_fetchrow($result))
{
$icons[$row['icons_id']]['img'] = $row['icons_url'];
$icons[$row['icons_id']]['width'] = (int) $row['icons_width'];
$icons[$row['icons_id']]['height'] = (int) $row['icons_height'];
$icons[$row['icons_id']]['display'] = (bool) $row['display_on_posting'];
}
- $db->sql_freeresult($result);
+ $this->db->sql_freeresult($result);
$this->driver->put('_icons', $icons);
}
@@ -124,15 +161,13 @@ class phpbb_cache_service
{
if (($ranks = $this->driver->get('_ranks')) === false)
{
- global $db;
-
$sql = 'SELECT *
FROM ' . RANKS_TABLE . '
ORDER BY rank_min DESC';
- $result = $db->sql_query($sql);
+ $result = $this->db->sql_query($sql);
$ranks = array();
- while ($row = $db->sql_fetchrow($result))
+ while ($row = $this->db->sql_fetchrow($result))
{
if ($row['rank_special'])
{
@@ -150,7 +185,7 @@ class phpbb_cache_service
);
}
}
- $db->sql_freeresult($result);
+ $this->db->sql_freeresult($result);
$this->driver->put('_ranks', $ranks);
}
@@ -169,8 +204,6 @@ class phpbb_cache_service
{
if (($extensions = $this->driver->get('_extensions')) === false)
{
- global $db;
-
$extensions = array(
'_allowed_post' => array(),
'_allowed_pm' => array(),
@@ -181,9 +214,9 @@ class phpbb_cache_service
FROM ' . EXTENSIONS_TABLE . ' e, ' . EXTENSION_GROUPS_TABLE . ' g
WHERE e.group_id = g.group_id
AND (g.allow_group = 1 OR g.allow_in_pm = 1)';
- $result = $db->sql_query($sql);
+ $result = $this->db->sql_query($sql);
- while ($row = $db->sql_fetchrow($result))
+ while ($row = $this->db->sql_fetchrow($result))
{
$extension = strtolower(trim($row['extension']));
@@ -210,7 +243,7 @@ class phpbb_cache_service
$extensions['_allowed_pm'][$extension] = 0;
}
}
- $db->sql_freeresult($result);
+ $this->db->sql_freeresult($result);
$this->driver->put('_extensions', $extensions);
}
@@ -275,9 +308,7 @@ class phpbb_cache_service
{
if (($bots = $this->driver->get('_bots')) === false)
{
- global $db;
-
- switch ($db->sql_layer)
+ switch ($this->db->sql_layer)
{
case 'mssql':
case 'mssql_odbc':
@@ -303,14 +334,14 @@ class phpbb_cache_service
ORDER BY LENGTH(bot_agent) DESC';
break;
}
- $result = $db->sql_query($sql);
+ $result = $this->db->sql_query($sql);
$bots = array();
- while ($row = $db->sql_fetchrow($result))
+ while ($row = $this->db->sql_fetchrow($result))
{
$bots[] = $row;
}
- $db->sql_freeresult($result);
+ $this->db->sql_freeresult($result);
$this->driver->put('_bots', $bots);
}
@@ -323,8 +354,6 @@ class phpbb_cache_service
*/
function obtain_cfg_items($style)
{
- global $config, $phpbb_root_path;
-
$parsed_array = $this->driver->get('_cfg_' . $style['style_path']);
if ($parsed_array === false)
@@ -332,14 +361,14 @@ class phpbb_cache_service
$parsed_array = array();
}
- $filename = $phpbb_root_path . 'styles/' . $style['style_path'] . '/style.cfg';
+ $filename = $this->phpbb_root_path . 'styles/' . $style['style_path'] . '/style.cfg';
if (!file_exists($filename))
{
return $parsed_array;
}
- if (!isset($parsed_array['filetime']) || (($config['load_tplcompile'] && @filemtime($filename) > $parsed_array['filetime'])))
+ if (!isset($parsed_array['filetime']) || (($this->config['load_tplcompile'] && @filemtime($filename) > $parsed_array['filetime'])))
{
// Re-parse cfg file
$parsed_array = parse_cfg_file($filename);
@@ -358,54 +387,20 @@ class phpbb_cache_service
{
if (($usernames = $this->driver->get('_disallowed_usernames')) === false)
{
- global $db;
-
$sql = 'SELECT disallow_username
FROM ' . DISALLOW_TABLE;
- $result = $db->sql_query($sql);
+ $result = $this->db->sql_query($sql);
$usernames = array();
- while ($row = $db->sql_fetchrow($result))
+ while ($row = $this->db->sql_fetchrow($result))
{
$usernames[] = str_replace('%', '.*?', preg_quote(utf8_clean_string($row['disallow_username']), '#'));
}
- $db->sql_freeresult($result);
+ $this->db->sql_freeresult($result);
$this->driver->put('_disallowed_usernames', $usernames);
}
return $usernames;
}
-
- /**
- * Obtain hooks...
- */
- function obtain_hooks()
- {
- global $phpbb_root_path, $phpEx;
-
- if (($hook_files = $this->driver->get('_hooks')) === false)
- {
- $hook_files = array();
-
- // Now search for hooks...
- $dh = @opendir($phpbb_root_path . 'includes/hooks/');
-
- if ($dh)
- {
- while (($file = readdir($dh)) !== false)
- {
- if (strpos($file, 'hook_') === 0 && substr($file, -(strlen($phpEx) + 1)) === '.' . $phpEx)
- {
- $hook_files[] = substr($file, 0, -(strlen($phpEx) + 1));
- }
- }
- closedir($dh);
- }
-
- $this->driver->put('_hooks', $hook_files);
- }
-
- return $hook_files;
- }
}
diff --git a/phpBB/includes/captcha/captcha_non_gd.php b/phpBB/includes/captcha/captcha_non_gd.php
index c2b97423e6..bb5067cafa 100644
--- a/phpBB/includes/captcha/captcha_non_gd.php
+++ b/phpBB/includes/captcha/captcha_non_gd.php
@@ -118,7 +118,7 @@ class captcha
$new_line = '';
$end = strlen($scanline) - ceil($width/2);
- for ($i = floor($width/2); $i < $end; $i++)
+ for ($i = (int) floor($width / 2); $i < $end; $i++)
{
$pixel = ord($scanline{$i});
diff --git a/phpBB/includes/config/config.php b/phpBB/includes/config/config.php
index 12a4a418b2..4b533dd55c 100644
--- a/phpBB/includes/config/config.php
+++ b/phpBB/includes/config/config.php
@@ -109,7 +109,7 @@ class phpbb_config implements ArrayAccess, IteratorAggregate, Countable
* @param String $key The configuration option's name
* @param bool $use_cache Whether this variable should be cached or if it
* changes too frequently to be efficiently cached
- * @return void
+ * @return null
*/
public function delete($key, $use_cache = true)
{
diff --git a/phpBB/includes/config/db.php b/phpBB/includes/config/db.php
index 993a764a7f..b18369a479 100644
--- a/phpBB/includes/config/db.php
+++ b/phpBB/includes/config/db.php
@@ -29,7 +29,7 @@ class phpbb_config_db extends phpbb_config
/**
* Database connection
- * @var dbal
+ * @var phpbb_db_driver
*/
protected $db;
@@ -42,11 +42,11 @@ class phpbb_config_db extends phpbb_config
/**
* Creates a configuration container with a default set of values
*
- * @param dbal $db Database connection
+ * @param phpbb_db_driver $db Database connection
* @param phpbb_cache_driver_interface $cache Cache instance
* @param string $table Configuration table name
*/
- public function __construct(dbal $db, phpbb_cache_driver_interface $cache, $table)
+ public function __construct(phpbb_db_driver $db, phpbb_cache_driver_interface $cache, $table)
{
$this->db = $db;
$this->cache = $cache;
@@ -96,7 +96,7 @@ class phpbb_config_db extends phpbb_config
* @param String $key The configuration option's name
* @param bool $use_cache Whether this variable should be cached or if it
* changes too frequently to be efficiently cached
- * @return void
+ * @return null
*/
public function delete($key, $use_cache = true)
{
diff --git a/phpBB/includes/controller/exception.php b/phpBB/includes/controller/exception.php
new file mode 100644
index 0000000000..faa8b6b584
--- /dev/null
+++ b/phpBB/includes/controller/exception.php
@@ -0,0 +1,24 @@
+<?php
+/**
+*
+* @package controller
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Controller exception class
+* @package phpBB3
+*/
+class phpbb_controller_exception extends RuntimeException
+{
+}
diff --git a/phpBB/includes/controller/helper.php b/phpBB/includes/controller/helper.php
new file mode 100644
index 0000000000..6cacc8fefa
--- /dev/null
+++ b/phpBB/includes/controller/helper.php
@@ -0,0 +1,117 @@
+<?php
+/**
+*
+* @package controller
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+* Controller helper class, contains methods that do things for controllers
+* @package phpBB3
+*/
+class phpbb_controller_helper
+{
+ /**
+ * Template object
+ * @var phpbb_template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var phpbb_user
+ */
+ protected $user;
+
+ /**
+ * phpBB root path
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * PHP extension
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param phpbb_template $template Template object
+ * @param phpbb_user $user User object
+ * @param string $phpbb_root_path phpBB root path
+ * @param string $php_ext PHP extension
+ */
+ public function __construct(phpbb_template $template, phpbb_user $user, $phpbb_root_path, $php_ext)
+ {
+ $this->template = $template;
+ $this->user = $user;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Automate setting up the page and creating the response object.
+ *
+ * @param string $handle The template handle to render
+ * @param string $page_title The title of the page to output
+ * @param int $status_code The status code to be sent to the page header
+ * @return Response object containing rendered page
+ */
+ public function render($template_file, $page_title = '', $status_code = 200)
+ {
+ page_header($page_title);
+
+ $this->template->set_filenames(array(
+ 'body' => $template_file,
+ ));
+
+ page_footer(true, false, false);
+
+ return new Response($this->template->assign_display('body'), $status_code);
+ }
+
+ /**
+ * Easily generate a URL
+ *
+ * @param array $url_parts Each array element is a 'folder'
+ * i.e. array('my', 'ext') maps to ./app.php/my/ext
+ * @param mixed $query The Query string, passed directly into the second
+ * argument of append_sid()
+ * @return string A URL that has already been run through append_sid()
+ */
+ public function url(array $url_parts, $query = '')
+ {
+ return append_sid($this->phpbb_root_path . implode('/', $url_parts), $query);
+ }
+
+ /**
+ * Output an error, effectively the same thing as trigger_error
+ *
+ * @param string $message The error message
+ * @param string $code The error code (e.g. 404, 500, 503, etc.)
+ * @return Response A Reponse instance
+ */
+ public function error($message, $code = 500)
+ {
+ $this->template->assign_vars(array(
+ 'MESSAGE_TEXT' => $message,
+ 'MESSAGE_TITLE' => $this->user->lang('INFORMATION'),
+ ));
+
+ return $this->render('message_body.html', $this->user->lang('INFORMATION'), $code);
+ }
+}
diff --git a/phpBB/includes/controller/provider.php b/phpBB/includes/controller/provider.php
new file mode 100644
index 0000000000..b2a5b9f6b2
--- /dev/null
+++ b/phpBB/includes/controller/provider.php
@@ -0,0 +1,82 @@
+<?php
+/**
+*
+* @package controller
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Loader\YamlFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+/**
+* Controller interface
+* @package phpBB3
+*/
+class phpbb_controller_provider
+{
+ /**
+ * YAML file(s) containing route information
+ * @var array
+ */
+ protected $routing_paths;
+
+ /**
+ * Construct method
+ *
+ * @param array() $routing_paths Array of strings containing paths
+ * to YAML files holding route information
+ */
+ public function __construct($routing_paths = array())
+ {
+ $this->routing_paths = $routing_paths;
+ }
+
+ /**
+ * Locate paths containing routing files
+ * This sets an internal property but does not return the paths.
+ *
+ * @return The current instance of this object for method chaining
+ */
+ public function import_paths_from_finder(phpbb_extension_finder $finder)
+ {
+ // We hardcode the path to the core config directory
+ // because the finder cannot find it
+ $this->routing_paths = array_merge(array('config'), array_map('dirname', array_keys($finder
+ ->directory('config')
+ ->prefix('routing')
+ ->suffix('yml')
+ ->find()
+ )));
+
+ return $this;
+ }
+
+ /**
+ * Get a list of controllers and return it
+ *
+ * @param string $base_path Base path to prepend to file paths
+ * @return array Array of controllers and their route information
+ */
+ public function find($base_path = '')
+ {
+ $routes = new RouteCollection;
+ foreach ($this->routing_paths as $path)
+ {
+ $loader = new YamlFileLoader(new FileLocator($base_path . $path));
+ $routes->addCollection($loader->load('routing.yml'));
+ }
+
+ return $routes;
+ }
+}
diff --git a/phpBB/includes/controller/resolver.php b/phpBB/includes/controller/resolver.php
new file mode 100644
index 0000000000..ee469aa9c8
--- /dev/null
+++ b/phpBB/includes/controller/resolver.php
@@ -0,0 +1,128 @@
+<?php
+/**
+*
+* @package controller
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+* Controller manager class
+* @package phpBB3
+*/
+class phpbb_controller_resolver implements ControllerResolverInterface
+{
+ /**
+ * User object
+ * @var phpbb_user
+ */
+ protected $user;
+
+ /**
+ * ContainerInterface object
+ * @var ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * Construct method
+ *
+ * @param phpbb_user $user User Object
+ * @param ContainerInterface $container ContainerInterface object
+ */
+ public function __construct(phpbb_user $user, ContainerInterface $container)
+ {
+ $this->user = $user;
+ $this->container = $container;
+ }
+
+ /**
+ * Load a controller callable
+ *
+ * @param Symfony\Component\HttpFoundation\Request $request Symfony Request object
+ * @return bool|Callable Callable or false
+ * @throws phpbb_controller_exception
+ */
+ public function getController(Request $request)
+ {
+ $controller = $request->attributes->get('_controller');
+
+ if (!$controller)
+ {
+ throw new phpbb_controller_exception($this->user->lang['CONTROLLER_NOT_SPECIFIED']);
+ }
+
+ // Require a method name along with the service name
+ if (stripos($controller, ':') === false)
+ {
+ throw new phpbb_controller_exception($this->user->lang['CONTROLLER_METHOD_NOT_SPECIFIED']);
+ }
+
+ list($service, $method) = explode(':', $controller);
+
+ if (!$this->container->has($service))
+ {
+ throw new phpbb_controller_exception($this->user->lang('CONTROLLER_SERVICE_UNDEFINED', $service));
+ }
+
+ $controller_object = $this->container->get($service);
+
+ return array($controller_object, $method);
+ }
+
+ /**
+ * Dependencies should be specified in the service definition and can be
+ * then accessed in __construct(). Arguments are sent through the URL path
+ * and should match the parameters of the method you are using as your
+ * controller.
+ *
+ * @param Symfony\Component\HttpFoundation\Request $request Symfony Request object
+ * @param mixed $controller A callable (controller class, method)
+ * @return bool False
+ * @throws phpbb_controller_exception
+ */
+ public function getArguments(Request $request, $controller)
+ {
+ // At this point, $controller contains the object and method name
+ list($object, $method) = $controller;
+ $mirror = new ReflectionMethod($object, $method);
+
+ $arguments = array();
+ $parameters = $mirror->getParameters();
+ $attributes = $request->attributes->all();
+ foreach ($parameters as $param)
+ {
+ if (array_key_exists($param->name, $attributes))
+ {
+ $arguments[] = $attributes[$param->name];
+ }
+ else if ($param->getClass() && $param->getClass()->isInstance($request))
+ {
+ $arguments[] = $request;
+ }
+ else if ($param->isDefaultValueAvailable())
+ {
+ $arguments[] = $param->getDefaultValue();
+ }
+ else
+ {
+ throw new phpbb_controller_exception($this->user->lang('CONTROLLER_ARGUMENT_VALUE_MISSING', $param->getPosition() + 1, get_class($object) . ':' . $method, $param->name));
+ }
+ }
+
+ return $arguments;
+ }
+}
diff --git a/phpBB/includes/cron/manager.php b/phpBB/includes/cron/manager.php
index ccaa4f3764..84c9650830 100644
--- a/phpBB/includes/cron/manager.php
+++ b/phpBB/includes/cron/manager.php
@@ -54,7 +54,7 @@ class phpbb_cron_manager
*
* @param array|Traversable $tasks Array of instances of phpbb_cron_task
*
- * @return void
+ * @return null
*/
public function load_tasks($tasks)
{
diff --git a/phpBB/includes/cron/task/core/prune_all_forums.php b/phpBB/includes/cron/task/core/prune_all_forums.php
index 252e16e57d..2c5d38cec0 100644
--- a/phpBB/includes/cron/task/core/prune_all_forums.php
+++ b/phpBB/includes/cron/task/core/prune_all_forums.php
@@ -37,9 +37,9 @@ class phpbb_cron_task_core_prune_all_forums extends phpbb_cron_task_base
* @param string $phpbb_root_path The root path
* @param string $php_ext The PHP extension
* @param phpbb_config $config The config
- * @param dbal $db The db connection
+ * @param phpbb_db_driver $db The db connection
*/
- public function __construct($phpbb_root_path, $php_ext, phpbb_config $config, dbal $db)
+ public function __construct($phpbb_root_path, $php_ext, phpbb_config $config, phpbb_db_driver $db)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
@@ -50,7 +50,7 @@ class phpbb_cron_task_core_prune_all_forums extends phpbb_cron_task_base
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
diff --git a/phpBB/includes/cron/task/core/prune_forum.php b/phpBB/includes/cron/task/core/prune_forum.php
index 41d60af921..e3c497f072 100644
--- a/phpBB/includes/cron/task/core/prune_forum.php
+++ b/phpBB/includes/cron/task/core/prune_forum.php
@@ -47,9 +47,9 @@ class phpbb_cron_task_core_prune_forum extends phpbb_cron_task_base implements p
* @param string $phpbb_root_path The root path
* @param string $php_ext The PHP extension
* @param phpbb_config $config The config
- * @param dbal $db The db connection
+ * @param phpbb_db_driver $db The db connection
*/
- public function __construct($phpbb_root_path, $php_ext, phpbb_config $config, dbal $db)
+ public function __construct($phpbb_root_path, $php_ext, phpbb_config $config, phpbb_db_driver $db)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
@@ -70,7 +70,7 @@ class phpbb_cron_task_core_prune_forum extends phpbb_cron_task_base implements p
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
@@ -138,7 +138,7 @@ class phpbb_cron_task_core_prune_forum extends phpbb_cron_task_base implements p
*
* @param phpbb_request_interface $request Request object.
*
- * @return void
+ * @return null
*/
public function parse_parameters(phpbb_request_interface $request)
{
diff --git a/phpBB/includes/cron/task/core/queue.php b/phpBB/includes/cron/task/core/queue.php
index c765660906..732f9c6bea 100644
--- a/phpBB/includes/cron/task/core/queue.php
+++ b/phpBB/includes/cron/task/core/queue.php
@@ -43,7 +43,7 @@ class phpbb_cron_task_core_queue extends phpbb_cron_task_base
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
diff --git a/phpBB/includes/cron/task/core/tidy_cache.php b/phpBB/includes/cron/task/core/tidy_cache.php
index 6017eea561..16a45dae7c 100644
--- a/phpBB/includes/cron/task/core/tidy_cache.php
+++ b/phpBB/includes/cron/task/core/tidy_cache.php
@@ -40,7 +40,7 @@ class phpbb_cron_task_core_tidy_cache extends phpbb_cron_task_base
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
diff --git a/phpBB/includes/cron/task/core/tidy_database.php b/phpBB/includes/cron/task/core/tidy_database.php
index 1d256f964f..b882e7b500 100644
--- a/phpBB/includes/cron/task/core/tidy_database.php
+++ b/phpBB/includes/cron/task/core/tidy_database.php
@@ -43,7 +43,7 @@ class phpbb_cron_task_core_tidy_database extends phpbb_cron_task_base
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
diff --git a/phpBB/includes/cron/task/core/tidy_search.php b/phpBB/includes/cron/task/core/tidy_search.php
index 2e5f3d79d5..3ec25aa021 100644
--- a/phpBB/includes/cron/task/core/tidy_search.php
+++ b/phpBB/includes/cron/task/core/tidy_search.php
@@ -38,10 +38,10 @@ class phpbb_cron_task_core_tidy_search extends phpbb_cron_task_base
* @param string $php_ext The PHP extension
* @param phpbb_auth $auth The auth
* @param phpbb_config $config The config
- * @param dbal $db The db connection
+ * @param phpbb_db_driver $db The db connection
* @param phpbb_user $user The user
*/
- public function __construct($phpbb_root_path, $php_ext, phpbb_auth $auth, phpbb_config $config, dbal $db, phpbb_user $user)
+ public function __construct($phpbb_root_path, $php_ext, phpbb_auth $auth, phpbb_config $config, phpbb_db_driver $db, phpbb_user $user)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
@@ -54,7 +54,7 @@ class phpbb_cron_task_core_tidy_search extends phpbb_cron_task_base
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
diff --git a/phpBB/includes/cron/task/core/tidy_sessions.php b/phpBB/includes/cron/task/core/tidy_sessions.php
index 13531aa30b..95f55235c9 100644
--- a/phpBB/includes/cron/task/core/tidy_sessions.php
+++ b/phpBB/includes/cron/task/core/tidy_sessions.php
@@ -40,7 +40,7 @@ class phpbb_cron_task_core_tidy_sessions extends phpbb_cron_task_base
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
diff --git a/phpBB/includes/cron/task/core/tidy_warnings.php b/phpBB/includes/cron/task/core/tidy_warnings.php
index 8dd0674fe5..2a7798e56e 100644
--- a/phpBB/includes/cron/task/core/tidy_warnings.php
+++ b/phpBB/includes/cron/task/core/tidy_warnings.php
@@ -45,7 +45,7 @@ class phpbb_cron_task_core_tidy_warnings extends phpbb_cron_task_base
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run()
{
diff --git a/phpBB/includes/cron/task/parametrized.php b/phpBB/includes/cron/task/parametrized.php
index 0714b2e701..5f0e46eafc 100644
--- a/phpBB/includes/cron/task/parametrized.php
+++ b/phpBB/includes/cron/task/parametrized.php
@@ -46,7 +46,7 @@ interface phpbb_cron_task_parametrized extends phpbb_cron_task
*
* @param phpbb_request_interface $request Request object.
*
- * @return void
+ * @return null
*/
public function parse_parameters(phpbb_request_interface $request);
}
diff --git a/phpBB/includes/cron/task/provider.php b/phpBB/includes/cron/task/provider.php
deleted file mode 100644
index 134723ebd1..0000000000
--- a/phpBB/includes/cron/task/provider.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* @ignore
-*/
-if (!defined('IN_PHPBB'))
-{
- exit;
-}
-
-use Symfony\Component\DependencyInjection\TaggedContainerInterface;
-
-/**
-* Provides cron manager with tasks
-*
-* Finds installed cron tasks and makes them available to the cron manager.
-*
-* @package phpBB3
-*/
-class phpbb_cron_task_provider implements IteratorAggregate
-{
- private $container;
-
- public function __construct(TaggedContainerInterface $container)
- {
- $this->container = $container;
- }
-
- /**
- * Retrieve an iterator over all items
- *
- * @return ArrayIterator An iterator for the array of cron tasks
- */
- public function getIterator()
- {
- $definitions = $this->container->findTaggedServiceIds('cron.task');
-
- $tasks = array();
- foreach ($definitions as $name => $definition)
- {
- $task = $this->container->get($name);
- if ($task instanceof phpbb_cron_task_base)
- {
- $task->set_name($name);
- }
-
- $tasks[] = $task;
- }
-
- return new ArrayIterator($tasks);
- }
-}
diff --git a/phpBB/includes/cron/task/task.php b/phpBB/includes/cron/task/task.php
index 7b08fed413..2d585df96d 100644
--- a/phpBB/includes/cron/task/task.php
+++ b/phpBB/includes/cron/task/task.php
@@ -31,7 +31,7 @@ interface phpbb_cron_task
/**
* Runs this cron task.
*
- * @return void
+ * @return null
*/
public function run();
diff --git a/phpBB/includes/db/db_tools.php b/phpBB/includes/db/db_tools.php
index 6df3aac9ce..a4bf40fcd7 100644
--- a/phpBB/includes/db/db_tools.php
+++ b/phpBB/includes/db/db_tools.php
@@ -300,7 +300,7 @@ class phpbb_db_tools
/**
* Constructor. Set DB Object and set {@link $return_statements return_statements}.
*
- * @param phpbb_dbal $db DBAL object
+ * @param phpbb_db_driver $db Database connection
* @param bool $return_statements True if only statements should be returned and no SQL being executed
*/
function phpbb_db_tools(&$db, $return_statements = false)
@@ -1817,6 +1817,22 @@ class phpbb_db_tools
case 'mssql':
case 'mssqlnative':
+ // remove default cosntraints first
+ // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
+ $statements[] = "DECLARE @drop_default_name VARCHAR(100), @cmd VARCHAR(1000)
+ SET @drop_default_name =
+ (SELECT so.name FROM sysobjects so
+ JOIN sysconstraints sc ON so.id = sc.constid
+ WHERE object_name(so.parent_obj) = '{$table_name}'
+ AND so.xtype = 'D'
+ AND sc.colid = (SELECT colid FROM syscolumns
+ WHERE id = object_id('{$table_name}')
+ AND name = '{$column_name}'))
+ IF @drop_default_name <> ''
+ BEGIN
+ SET @cmd = 'ALTER TABLE [{$table_name}] DROP CONSTRAINT [' + @drop_default_name + ']'
+ EXEC(@cmd)
+ END";
$statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
break;
diff --git a/phpBB/includes/db/dbal.php b/phpBB/includes/db/driver/driver.php
index ef1dd7d14d..8dda94bc2c 100644
--- a/phpBB/includes/db/dbal.php
+++ b/phpBB/includes/db/driver/driver.php
@@ -19,7 +19,7 @@ if (!defined('IN_PHPBB'))
* Database Abstraction Layer
* @package dbal
*/
-class dbal
+class phpbb_db_driver
{
var $db_connect_id;
var $query_result;
@@ -72,17 +72,17 @@ class dbal
/**
* Constructor
*/
- function dbal()
+ function __construct()
{
$this->num_queries = array(
- 'cached' => 0,
- 'normal' => 0,
- 'total' => 0,
+ 'cached' => 0,
+ 'normal' => 0,
+ 'total' => 0,
);
// Fill default sql layer based on the class being called.
// This can be changed by the specified layer itself later if needed.
- $this->sql_layer = substr(get_class($this), 5);
+ $this->sql_layer = substr(get_class($this), strlen('phpbb_db_driver_'));
// Do not change this please! This variable is used to easy the use of it - and is hardcoded.
$this->any_char = chr(0) . '%';
@@ -206,7 +206,7 @@ class dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_rowseek($rownum, $query_id);
}
@@ -256,7 +256,7 @@ class dbal
$this->sql_rowseek($rownum, $query_id);
}
- if (!is_object($query_id) && $cache->sql_exists($query_id))
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
{
return $cache->sql_fetchfield($query_id, $field);
}
@@ -822,7 +822,7 @@ class dbal
*/
function sql_report($mode, $query = '')
{
- global $cache, $starttime, $phpbb_root_path, $user;
+ global $cache, $starttime, $phpbb_root_path, $phpbb_admin_path, $user;
global $request;
if (is_object($request) && !$request->variable('explain', false))
@@ -852,7 +852,7 @@ class dbal
<head>
<meta charset="utf-8">
<title>SQL Report</title>
- <link href="' . $phpbb_root_path . 'adm/style/admin.css" rel="stylesheet" type="text/css" media="screen" />
+ <link href="' . htmlspecialchars($phpbb_admin_path) . 'style/admin.css" rel="stylesheet" type="text/css" media="screen" />
</head>
<body id="errorpage">
<div id="wrap">
@@ -1042,8 +1042,3 @@ class dbal
return $rows_total;
}
}
-
-/**
-* This variable holds the class name to use later
-*/
-$sql_db = (!empty($dbms)) ? 'dbal_' . basename($dbms) : 'dbal';
diff --git a/phpBB/includes/db/firebird.php b/phpBB/includes/db/driver/firebird.php
index 5728eb901c..787c28b812 100644
--- a/phpBB/includes/db/firebird.php
+++ b/phpBB/includes/db/driver/firebird.php
@@ -15,14 +15,12 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* Firebird/Interbase Database Abstraction Layer
* Minimum Requirement is Firebird 2.1
* @package dbal
*/
-class dbal_firebird extends dbal
+class phpbb_db_driver_firebird extends phpbb_db_driver
{
var $last_query_text = '';
var $service_handle = false;
@@ -156,7 +154,7 @@ class dbal_firebird extends dbal
}
$this->last_query_text = $query;
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -269,10 +267,10 @@ class dbal_firebird extends dbal
}
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -332,7 +330,7 @@ class dbal_firebird extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -398,7 +396,7 @@ class dbal_firebird extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
diff --git a/phpBB/includes/db/mssql.php b/phpBB/includes/db/driver/mssql.php
index 1ec8517308..89c2c2351b 100644
--- a/phpBB/includes/db/mssql.php
+++ b/phpBB/includes/db/driver/mssql.php
@@ -15,20 +15,26 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* MSSQL Database Abstraction Layer
* Minimum Requirement is MSSQL 2000+
* @package dbal
*/
-class dbal_mssql extends dbal
+class phpbb_db_driver_mssql extends phpbb_db_driver
{
+ var $connect_error = '';
+
/**
* Connect to server
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
+ if (!function_exists('mssql_connect'))
+ {
+ $this->connect_error = 'mssql_connect function does not exist, is mssql extension installed?';
+ return $this->sql_error('');
+ }
+
$this->persistency = $persistency;
$this->user = $sqluser;
$this->dbname = $database;
@@ -144,7 +150,7 @@ class dbal_mssql extends dbal
$this->sql_report('start', $query);
}
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -159,10 +165,10 @@ class dbal_mssql extends dbal
$this->sql_report('stop', $query);
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -234,7 +240,7 @@ class dbal_mssql extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -271,7 +277,7 @@ class dbal_mssql extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_rowseek($rownum, $query_id);
}
@@ -310,7 +316,7 @@ class dbal_mssql extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
@@ -355,34 +361,44 @@ class dbal_mssql extends dbal
*/
function _sql_error()
{
- $error = array(
- 'message' => @mssql_get_last_message(),
- 'code' => ''
- );
-
- // Get error code number
- $result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id);
- if ($result_id)
+ if (function_exists('mssql_get_last_message'))
{
- $row = @mssql_fetch_assoc($result_id);
- $error['code'] = $row['code'];
- @mssql_free_result($result_id);
- }
+ $error = array(
+ 'message' => @mssql_get_last_message(),
+ 'code' => '',
+ );
- // Get full error message if possible
- $sql = 'SELECT CAST(description as varchar(255)) as message
- FROM master.dbo.sysmessages
- WHERE error = ' . $error['code'];
- $result_id = @mssql_query($sql);
-
- if ($result_id)
- {
- $row = @mssql_fetch_assoc($result_id);
- if (!empty($row['message']))
+ // Get error code number
+ $result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id);
+ if ($result_id)
{
- $error['message'] .= '<br />' . $row['message'];
+ $row = @mssql_fetch_assoc($result_id);
+ $error['code'] = $row['code'];
+ @mssql_free_result($result_id);
}
- @mssql_free_result($result_id);
+
+ // Get full error message if possible
+ $sql = 'SELECT CAST(description as varchar(255)) as message
+ FROM master.dbo.sysmessages
+ WHERE error = ' . $error['code'];
+ $result_id = @mssql_query($sql);
+
+ if ($result_id)
+ {
+ $row = @mssql_fetch_assoc($result_id);
+ if (!empty($row['message']))
+ {
+ $error['message'] .= '<br />' . $row['message'];
+ }
+ @mssql_free_result($result_id);
+ }
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
}
return $error;
diff --git a/phpBB/includes/db/mssql_odbc.php b/phpBB/includes/db/driver/mssql_odbc.php
index 7c1ffbc808..f7834443eb 100644
--- a/phpBB/includes/db/mssql_odbc.php
+++ b/phpBB/includes/db/driver/mssql_odbc.php
@@ -15,8 +15,6 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* Unified ODBC functions
* Unified ODBC functions support any database having ODBC driver, for example Adabas D, IBM DB2, iODBC, Solid, Sybase SQL Anywhere...
@@ -28,9 +26,10 @@ include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
*
* @package dbal
*/
-class dbal_mssql_odbc extends dbal
+class phpbb_db_driver_mssql_odbc extends phpbb_db_driver
{
var $last_query_text = '';
+ var $connect_error = '';
/**
* Connect to server
@@ -67,7 +66,24 @@ class dbal_mssql_odbc extends dbal
@ini_set('odbc.defaultlrl', $max_size);
}
- $this->db_connect_id = ($this->persistency) ? @odbc_pconnect($this->server, $this->user, $sqlpassword) : @odbc_connect($this->server, $this->user, $sqlpassword);
+ if ($this->persistency)
+ {
+ if (!function_exists('odbc_pconnect'))
+ {
+ $this->connect_error = 'odbc_pconnect function does not exist, is odbc extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @odbc_pconnect($this->server, $this->user, $sqlpassword);
+ }
+ else
+ {
+ if (!function_exists('odbc_connect'))
+ {
+ $this->connect_error = 'odbc_connect function does not exist, is odbc extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @odbc_connect($this->server, $this->user, $sqlpassword);
+ }
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
}
@@ -163,7 +179,7 @@ class dbal_mssql_odbc extends dbal
}
$this->last_query_text = $query;
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -178,10 +194,10 @@ class dbal_mssql_odbc extends dbal
$this->sql_report('stop', $query);
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -254,7 +270,7 @@ class dbal_mssql_odbc extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -295,7 +311,7 @@ class dbal_mssql_odbc extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
@@ -349,10 +365,22 @@ class dbal_mssql_odbc extends dbal
*/
function _sql_error()
{
- return array(
- 'message' => @odbc_errormsg(),
- 'code' => @odbc_error()
- );
+ if (function_exists('odbc_errormsg'))
+ {
+ $error = array(
+ 'message' => @odbc_errormsg(),
+ 'code' => @odbc_error(),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
}
/**
diff --git a/phpBB/includes/db/mssqlnative.php b/phpBB/includes/db/driver/mssqlnative.php
index e9191fae8a..656cbd2437 100644
--- a/phpBB/includes/db/mssqlnative.php
+++ b/phpBB/includes/db/driver/mssqlnative.php
@@ -19,8 +19,6 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* Prior to version 1.1 the SQL Server Native PHP driver didn't support sqlsrv_num_rows, or cursor based seeking so we recall all rows into an array
* and maintain our own cursor index into that array.
@@ -193,21 +191,23 @@ class result_mssqlnative
/**
* @package dbal
*/
-class dbal_mssqlnative extends dbal
+class phpbb_db_driver_mssqlnative extends phpbb_db_driver
{
var $m_insert_id = NULL;
var $last_query_text = '';
var $query_options = array();
+ var $connect_error = '';
/**
* Connect to server
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
- # Test for driver support, to avoid suppressed fatal error
+ // Test for driver support, to avoid suppressed fatal error
if (!function_exists('sqlsrv_connect'))
{
- trigger_error('Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx\n', E_USER_ERROR);
+ $this->connect_error = 'Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx';
+ return $this->sql_error('');
}
//set up connection variables
@@ -317,7 +317,7 @@ class dbal_mssqlnative extends dbal
}
$this->last_query_text = $query;
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -337,7 +337,7 @@ class dbal_mssqlnative extends dbal
if ($cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -521,31 +521,43 @@ class dbal_mssqlnative extends dbal
*/
function _sql_error()
{
- $errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS);
- $error_message = '';
- $code = 0;
-
- if ($errors != null)
+ if (function_exists('sqlsrv_errors'))
{
- foreach ($errors as $error)
+ $errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS);
+ $error_message = '';
+ $code = 0;
+
+ if ($errors != null)
{
- $error_message .= "SQLSTATE: ".$error[ 'SQLSTATE']."\n";
- $error_message .= "code: ".$error[ 'code']."\n";
- $code = $error['code'];
- $error_message .= "message: ".$error[ 'message']."\n";
+ foreach ($errors as $error)
+ {
+ $error_message .= "SQLSTATE: " . $error[ 'SQLSTATE'] . "\n";
+ $error_message .= "code: " . $error[ 'code'] . "\n";
+ $code = $error['code'];
+ $error_message .= "message: " . $error[ 'message'] . "\n";
+ }
+ $this->last_error_result = $error_message;
+ $error = $this->last_error_result;
}
- $this->last_error_result = $error_message;
- $error = $this->last_error_result;
+ else
+ {
+ $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
+ }
+
+ $error = array(
+ 'message' => $error,
+ 'code' => $code,
+ );
}
else
{
- $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
}
- return array(
- 'message' => $error,
- 'code' => $code,
- );
+ return $error;
}
/**
diff --git a/phpBB/includes/db/mysql.php b/phpBB/includes/db/driver/mysql.php
index f685ab055c..9de7283a42 100644
--- a/phpBB/includes/db/mysql.php
+++ b/phpBB/includes/db/driver/mysql.php
@@ -15,8 +15,6 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* MySQL4 Database Abstraction Layer
* Compatible with:
@@ -26,9 +24,10 @@ include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
* MySQL 5.0+
* @package dbal
*/
-class dbal_mysql extends dbal
+class phpbb_db_driver_mysql extends phpbb_db_driver
{
var $multi_insert = true;
+ var $connect_error = '';
/**
* Connect to server
@@ -43,7 +42,24 @@ class dbal_mysql extends dbal
$this->sql_layer = 'mysql4';
- $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword) : @mysql_connect($this->server, $this->user, $sqlpassword, $new_link);
+ if ($this->persistency)
+ {
+ if (!function_exists('mysql_pconnect'))
+ {
+ $this->connect_error = 'mysql_pconnect function does not exist, is mysql extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @mysql_pconnect($this->server, $this->user, $sqlpassword);
+ }
+ else
+ {
+ if (!function_exists('mysql_connect'))
+ {
+ $this->connect_error = 'mysql_connect function does not exist, is mysql extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @mysql_connect($this->server, $this->user, $sqlpassword, $new_link);
+ }
if ($this->db_connect_id && $this->dbname != '')
{
@@ -172,7 +188,7 @@ class dbal_mysql extends dbal
$this->sql_report('start', $query);
}
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -187,10 +203,10 @@ class dbal_mysql extends dbal
$this->sql_report('stop', $query);
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -249,7 +265,7 @@ class dbal_mysql extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -270,7 +286,7 @@ class dbal_mysql extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_rowseek($rownum, $query_id);
}
@@ -298,7 +314,7 @@ class dbal_mysql extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
@@ -426,18 +442,29 @@ class dbal_mysql extends dbal
*/
function _sql_error()
{
- if (!$this->db_connect_id)
+ if ($this->db_connect_id)
+ {
+ $error = array(
+ 'message' => @mysql_error($this->db_connect_id),
+ 'code' => @mysql_errno($this->db_connect_id),
+ );
+ }
+ else if (function_exists('mysql_error'))
{
- return array(
+ $error = array(
'message' => @mysql_error(),
- 'code' => @mysql_errno()
+ 'code' => @mysql_errno(),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
);
}
- return array(
- 'message' => @mysql_error($this->db_connect_id),
- 'code' => @mysql_errno($this->db_connect_id)
- );
+ return $error;
}
/**
diff --git a/phpBB/includes/db/mysqli.php b/phpBB/includes/db/driver/mysqli.php
index 6d81b8bc3e..7448bf1670 100644
--- a/phpBB/includes/db/mysqli.php
+++ b/phpBB/includes/db/driver/mysqli.php
@@ -15,23 +15,28 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* MySQLi Database Abstraction Layer
* mysqli-extension has to be compiled with:
* MySQL 4.1+ or MySQL 5.0+
* @package dbal
*/
-class dbal_mysqli extends dbal
+class phpbb_db_driver_mysqli extends phpbb_db_driver
{
var $multi_insert = true;
+ var $connect_error = '';
/**
* Connect to server
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false , $new_link = false)
{
+ if (!function_exists('mysqli_connect'))
+ {
+ $this->connect_error = 'mysqli_connect function does not exist, is mysqli extension installed?';
+ return $this->sql_error('');
+ }
+
// Mysqli extension supports persistent connection since PHP 5.3.0
$this->persistency = (version_compare(PHP_VERSION, '5.3.0', '>=')) ? $persistency : false;
$this->user = $sqluser;
@@ -179,7 +184,7 @@ class dbal_mysqli extends dbal
$this->sql_report('start', $query);
}
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -194,9 +199,9 @@ class dbal_mysqli extends dbal
$this->sql_report('stop', $query);
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
}
else if (defined('DEBUG'))
@@ -251,7 +256,7 @@ class dbal_mysqli extends dbal
$query_id = $this->query_result;
}
- if (!is_object($query_id) && $cache->sql_exists($query_id))
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -278,7 +283,7 @@ class dbal_mysqli extends dbal
$query_id = $this->query_result;
}
- if (!is_object($query_id) && $cache->sql_exists($query_id))
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
{
return $cache->sql_rowseek($rownum, $query_id);
}
@@ -306,7 +311,7 @@ class dbal_mysqli extends dbal
$query_id = $this->query_result;
}
- if (!is_object($query_id) && $cache->sql_exists($query_id))
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
@@ -423,18 +428,29 @@ class dbal_mysqli extends dbal
*/
function _sql_error()
{
- if (!$this->db_connect_id)
+ if ($this->db_connect_id)
{
- return array(
+ $error = array(
+ 'message' => @mysqli_error($this->db_connect_id),
+ 'code' => @mysqli_errno($this->db_connect_id)
+ );
+ }
+ else if (function_exists('mysqli_connect_error'))
+ {
+ $error = array(
'message' => @mysqli_connect_error(),
- 'code' => @mysqli_connect_errno()
+ 'code' => @mysqli_connect_errno(),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
);
}
- return array(
- 'message' => @mysqli_error($this->db_connect_id),
- 'code' => @mysqli_errno($this->db_connect_id)
- );
+ return $error;
}
/**
diff --git a/phpBB/includes/db/oracle.php b/phpBB/includes/db/driver/oracle.php
index 6d9339b2d8..e21e07055d 100644
--- a/phpBB/includes/db/oracle.php
+++ b/phpBB/includes/db/driver/oracle.php
@@ -15,15 +15,14 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* Oracle Database Abstraction Layer
* @package dbal
*/
-class dbal_oracle extends dbal
+class phpbb_db_driver_oracle extends phpbb_db_driver
{
var $last_query_text = '';
+ var $connect_error = '';
/**
* Connect to server
@@ -47,7 +46,33 @@ class dbal_oracle extends dbal
$connect = $sqlserver . (($port) ? ':' . $port : '') . '/' . $database;
}
- $this->db_connect_id = ($new_link) ? @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8') : (($this->persistency) ? @ociplogon($this->user, $sqlpassword, $connect, 'UTF8') : @ocilogon($this->user, $sqlpassword, $connect, 'UTF8'));
+ if ($new_link)
+ {
+ if (!function_exists('ocinlogon'))
+ {
+ $this->connect_error = 'ocinlogon function does not exist, is oci extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8');
+ }
+ else if ($this->persistency)
+ {
+ if (!function_exists('ociplogon'))
+ {
+ $this->connect_error = 'ociplogon function does not exist, is oci extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @ociplogon($this->user, $sqlpassword, $connect, 'UTF8');
+ }
+ else
+ {
+ if (!function_exists('ocilogon'))
+ {
+ $this->connect_error = 'ocilogon function does not exist, is oci extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @ocilogon($this->user, $sqlpassword, $connect, 'UTF8');
+ }
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
}
@@ -242,7 +267,7 @@ class dbal_oracle extends dbal
}
$this->last_query_text = $query;
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -418,10 +443,10 @@ class dbal_oracle extends dbal
$this->sql_report('stop', $query);
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -473,7 +498,7 @@ class dbal_oracle extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -525,7 +550,7 @@ class dbal_oracle extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_rowseek($rownum, $query_id);
}
@@ -594,7 +619,7 @@ class dbal_oracle extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
@@ -646,17 +671,27 @@ class dbal_oracle extends dbal
*/
function _sql_error()
{
- $error = @ocierror();
- $error = (!$error) ? @ocierror($this->query_result) : $error;
- $error = (!$error) ? @ocierror($this->db_connect_id) : $error;
-
- if ($error)
+ if (function_exists('ocierror'))
{
- $this->last_error_result = $error;
+ $error = @ocierror();
+ $error = (!$error) ? @ocierror($this->query_result) : $error;
+ $error = (!$error) ? @ocierror($this->db_connect_id) : $error;
+
+ if ($error)
+ {
+ $this->last_error_result = $error;
+ }
+ else
+ {
+ $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
+ }
}
else
{
- $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
}
return $error;
diff --git a/phpBB/includes/db/postgres.php b/phpBB/includes/db/driver/postgres.php
index 8dfbfc3b60..14854d179d 100644
--- a/phpBB/includes/db/postgres.php
+++ b/phpBB/includes/db/driver/postgres.php
@@ -15,19 +15,12 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
-if (!class_exists('phpbb_error_collector'))
-{
- include($phpbb_root_path . 'includes/error_collector.' . $phpEx);
-}
-
/**
* PostgreSQL Database Abstraction Layer
* Minimum Requirement is Version 7.3+
* @package dbal
*/
-class dbal_postgres extends dbal
+class phpbb_db_driver_postgres extends phpbb_db_driver
{
var $last_query_text = '';
var $connect_error = '';
@@ -200,7 +193,7 @@ class dbal_postgres extends dbal
}
$this->last_query_text = $query;
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -215,10 +208,10 @@ class dbal_postgres extends dbal
$this->sql_report('stop', $query);
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -285,7 +278,7 @@ class dbal_postgres extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -306,7 +299,7 @@ class dbal_postgres extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_rowseek($rownum, $query_id);
}
@@ -355,7 +348,7 @@ class dbal_postgres extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
diff --git a/phpBB/includes/db/sqlite.php b/phpBB/includes/db/driver/sqlite.php
index 5fc89ced18..7188f0daa2 100644
--- a/phpBB/includes/db/sqlite.php
+++ b/phpBB/includes/db/driver/sqlite.php
@@ -15,15 +15,15 @@ if (!defined('IN_PHPBB'))
exit;
}
-include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
-
/**
* Sqlite Database Abstraction Layer
* Minimum Requirement: 2.8.2+
* @package dbal
*/
-class dbal_sqlite extends dbal
+class phpbb_db_driver_sqlite extends phpbb_db_driver
{
+ var $connect_error = '';
+
/**
* Connect to server
*/
@@ -35,7 +35,24 @@ class dbal_sqlite extends dbal
$this->dbname = $database;
$error = '';
- $this->db_connect_id = ($this->persistency) ? @sqlite_popen($this->server, 0666, $error) : @sqlite_open($this->server, 0666, $error);
+ if ($this->persistency)
+ {
+ if (!function_exists('sqlite_popen'))
+ {
+ $this->connect_error = 'sqlite_popen function does not exist, is sqlite extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @sqlite_popen($this->server, 0666, $error);
+ }
+ else
+ {
+ if (!function_exists('sqlite_open'))
+ {
+ $this->connect_error = 'sqlite_open function does not exist, is sqlite extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @sqlite_open($this->server, 0666, $error);
+ }
if ($this->db_connect_id)
{
@@ -117,7 +134,7 @@ class dbal_sqlite extends dbal
$this->sql_report('start', $query);
}
- $this->query_result = ($cache_ttl) ? $cache->sql_load($query) : false;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
$this->sql_add_num_queries($this->query_result);
if ($this->query_result === false)
@@ -132,10 +149,10 @@ class dbal_sqlite extends dbal
$this->sql_report('stop', $query);
}
- if ($cache_ttl)
+ if ($cache && $cache_ttl)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($query, $this->query_result, $cache_ttl);
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
}
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
{
@@ -193,7 +210,7 @@ class dbal_sqlite extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_fetchrow($query_id);
}
@@ -214,7 +231,7 @@ class dbal_sqlite extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_rowseek($rownum, $query_id);
}
@@ -242,7 +259,7 @@ class dbal_sqlite extends dbal
$query_id = $this->query_result;
}
- if ($cache->sql_exists($query_id))
+ if ($cache && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
}
@@ -280,10 +297,22 @@ class dbal_sqlite extends dbal
*/
function _sql_error()
{
- return array(
- 'message' => @sqlite_error_string(@sqlite_last_error($this->db_connect_id)),
- 'code' => @sqlite_last_error($this->db_connect_id)
- );
+ if (function_exists('sqlite_error_string'))
+ {
+ $error = array(
+ 'message' => @sqlite_error_string(@sqlite_last_error($this->db_connect_id)),
+ 'code' => @sqlite_last_error($this->db_connect_id),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
}
/**
diff --git a/phpBB/includes/di/extension/config.php b/phpBB/includes/di/extension/config.php
new file mode 100644
index 0000000000..6c272a6588
--- /dev/null
+++ b/phpBB/includes/di/extension/config.php
@@ -0,0 +1,84 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+/**
+* Container config extension
+*/
+class phpbb_di_extension_config extends Extension
+{
+ public function __construct($config_file)
+ {
+ $this->config_file = $config_file;
+ }
+
+ /**
+ * Loads a specific configuration.
+ *
+ * @param array $config An array of configuration values
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ *
+ * @throws InvalidArgumentException When provided tag is not defined in this extension
+ */
+ public function load(array $config, ContainerBuilder $container)
+ {
+ require($this->config_file);
+
+ $container->setParameter('core.adm_relative_path', (isset($phpbb_adm_relative_path) ? $phpbb_adm_relative_path : 'adm/'));
+ $container->setParameter('core.table_prefix', $table_prefix);
+ $container->setParameter('cache.driver.class', $this->convert_30_acm_type($acm_type));
+ $container->setParameter('dbal.driver.class', phpbb_convert_30_dbms_to_31($dbms));
+ $container->setParameter('dbal.dbhost', $dbhost);
+ $container->setParameter('dbal.dbuser', $dbuser);
+ $container->setParameter('dbal.dbpasswd', $dbpasswd);
+ $container->setParameter('dbal.dbname', $dbname);
+ $container->setParameter('dbal.dbport', $dbport);
+ $container->setParameter('dbal.new_link', defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK);
+ }
+
+ /**
+ * Returns the recommended alias to use in XML.
+ *
+ * This alias is also the mandatory prefix to use when using YAML.
+ *
+ * @return string The alias
+ */
+ public function getAlias()
+ {
+ return 'config';
+ }
+
+ /**
+ * Convert 3.0 ACM type to 3.1 cache driver class name
+ *
+ * @param string $acm_type ACM type
+ * @return cache driver class
+ */
+ protected function convert_30_acm_type($acm_type)
+ {
+ if (preg_match('#^[a-z]+$#', $acm_type))
+ {
+ return 'phpbb_cache_driver_'.$acm_type;
+ }
+
+ return $acm_type;
+ }
+}
diff --git a/phpBB/includes/di/extension/core.php b/phpBB/includes/di/extension/core.php
new file mode 100644
index 0000000000..9c36ba2fc4
--- /dev/null
+++ b/phpBB/includes/di/extension/core.php
@@ -0,0 +1,69 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+/**
+* Container core extension
+*/
+class phpbb_di_extension_core extends Extension
+{
+ /**
+ * phpBB Root path
+ * @var string
+ */
+ protected $root_path;
+
+ /**
+ * Constructor
+ *
+ * @param string $root_path Root path
+ */
+ public function __construct($root_path)
+ {
+ $this->root_path = $root_path;
+ }
+
+ /**
+ * Loads a specific configuration.
+ *
+ * @param array $config An array of configuration values
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ *
+ * @throws InvalidArgumentException When provided tag is not defined in this extension
+ */
+ public function load(array $config, ContainerBuilder $container)
+ {
+ $loader = new YamlFileLoader($container, new FileLocator(phpbb_realpath($this->root_path . 'config')));
+ $loader->load('services.yml');
+ }
+
+ /**
+ * Returns the recommended alias to use in XML.
+ *
+ * This alias is also the mandatory prefix to use when using YAML.
+ *
+ * @return string The alias
+ */
+ public function getAlias()
+ {
+ return 'core';
+ }
+}
diff --git a/phpBB/includes/di/extension/ext.php b/phpBB/includes/di/extension/ext.php
new file mode 100644
index 0000000000..7d9b433751
--- /dev/null
+++ b/phpBB/includes/di/extension/ext.php
@@ -0,0 +1,69 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+/**
+* Container ext extension
+*/
+class phpbb_di_extension_ext extends Extension
+{
+ protected $paths = array();
+
+ public function __construct($enabled_extensions)
+ {
+ foreach ($enabled_extensions as $ext => $path)
+ {
+ $this->paths[] = $path;
+ }
+ }
+
+ /**
+ * Loads a specific configuration.
+ *
+ * @param array $config An array of configuration values
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ *
+ * @throws InvalidArgumentException When provided tag is not defined in this extension
+ */
+ public function load(array $config, ContainerBuilder $container)
+ {
+ foreach ($this->paths as $path)
+ {
+ if (file_exists($path . '/config/services.yml'))
+ {
+ $loader = new YamlFileLoader($container, new FileLocator(phpbb_realpath($path . '/config')));
+ $loader->load('services.yml');
+ }
+ }
+ }
+
+ /**
+ * Returns the recommended alias to use in XML.
+ *
+ * This alias is also the mandatory prefix to use when using YAML.
+ *
+ * @return string The alias
+ */
+ public function getAlias()
+ {
+ return 'ext';
+ }
+}
diff --git a/phpBB/includes/di/pass/collection_pass.php b/phpBB/includes/di/pass/collection_pass.php
new file mode 100644
index 0000000000..63a5c7dfc4
--- /dev/null
+++ b/phpBB/includes/di/pass/collection_pass.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+/**
+* Appends an add method call to the definition of each collection service for
+* the services tagged with the appropriate name defined in the collection's
+* service_collection tag.
+*/
+class phpbb_di_pass_collection_pass implements CompilerPassInterface
+{
+ /**
+ * Modify the container before it is passed to the rest of the code
+ *
+ * @param ContainerBuilder $container ContainerBuilder object
+ * @return null
+ */
+ public function process(ContainerBuilder $container)
+ {
+ foreach ($container->findTaggedServiceIds('service_collection') as $id => $data)
+ {
+ $definition = $container->getDefinition($id);
+
+ foreach ($container->findTaggedServiceIds($data[0]['tag']) as $service_id => $service_data)
+ {
+ $definition->addMethodCall('add', array($service_id));
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/di/pass/kernel_pass.php b/phpBB/includes/di/pass/kernel_pass.php
new file mode 100644
index 0000000000..a701ebcfa6
--- /dev/null
+++ b/phpBB/includes/di/pass/kernel_pass.php
@@ -0,0 +1,68 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+class phpbb_di_pass_kernel_pass implements CompilerPassInterface
+{
+ /**
+ * Modify the container before it is passed to the rest of the code
+ *
+ * @param ContainerBuilder $container ContainerBuilder object
+ * @return null
+ */
+ public function process(ContainerBuilder $container)
+ {
+ $definition = $container->getDefinition('dispatcher');
+
+ foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $events)
+ {
+ foreach ($events as $event)
+ {
+ $priority = isset($event['priority']) ? $event['priority'] : 0;
+
+ if (!isset($event['event']))
+ {
+ throw new InvalidArgumentException(sprintf('Service "%1$s" must define the "event" attribute on "kernel.event_listener" tags.', $id));
+ }
+
+ if (!isset($event['method']))
+ {
+ throw new InvalidArgumentException(sprintf('Service "%1$s" must define the "method" attribute on "kernel.event_listener" tags.', $id));
+ }
+
+ $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
+ }
+ }
+
+ foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $id => $attributes)
+ {
+ // We must assume that the class value has been correctly filled, even if the service is created by a factory
+ $class = $container->getDefinition($id)->getClass();
+
+ $refClass = new ReflectionClass($class);
+ $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
+ if (!$refClass->implementsInterface($interface))
+ {
+ throw new InvalidArgumentException(sprintf('Service "%1$s" must implement interface "%2$s".', $id, $interface));
+ }
+
+ $definition->addMethodCall('addSubscriberService', array($id, $class));
+ }
+ }
+}
diff --git a/phpBB/includes/di/processor/config.php b/phpBB/includes/di/processor/config.php
deleted file mode 100644
index 22b6252a6d..0000000000
--- a/phpBB/includes/di/processor/config.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* @ignore
-*/
-if (!defined('IN_PHPBB'))
-{
- exit;
-}
-
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-/**
-* Configure the container for phpBB's services though
-* user-defined parameters defined in the config.php file.
-*/
-class phpbb_di_processor_config implements phpbb_di_processor_interface
-{
- private $config_file;
- private $phpbb_root_path;
- private $php_ext;
-
- /**
- * Constructor.
- *
- * @param string $config_file The config file
- * @param string $phpbb_root_path The root path
- * @param string $php_ext The PHP extension
- */
- public function __construct($config_file, $phpbb_root_path, $php_ext)
- {
- $this->config_file = $config_file;
- $this->phpbb_root_path = $phpbb_root_path;
- $this->php_ext = $php_ext;
- }
-
- /**
- * @inheritdoc
- */
- public function process(ContainerBuilder $container)
- {
- require $this->config_file;
-
- $container->setParameter('core.root_path', $this->phpbb_root_path);
- $container->setParameter('core.php_ext', $this->php_ext);
-
- $container->setParameter('core.table_prefix', $table_prefix);
- $container->setParameter('cache.driver.class', $this->fix_acm_type($acm_type));
- $container->setParameter('dbal.driver.class', 'dbal_'.$dbms);
- $container->setParameter('dbal.dbhost', $dbhost);
- $container->setParameter('dbal.dbuser', $dbuser);
- $container->setParameter('dbal.dbpasswd', $dbpasswd);
- $container->setParameter('dbal.dbname', $dbname);
- $container->setParameter('dbal.dbport', $dbport);
- $container->setParameter('dbal.new_link', defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK);
-
- $container->set('container', $container);
- }
-
- protected function fix_acm_type($acm_type)
- {
- if (preg_match('#^[a-z]+$#', $acm_type))
- {
- return 'phpbb_cache_driver_'.$acm_type;
- }
-
- return $acm_type;
- }
-}
diff --git a/phpBB/includes/di/processor/ext.php b/phpBB/includes/di/processor/ext.php
deleted file mode 100644
index e69a3d73b3..0000000000
--- a/phpBB/includes/di/processor/ext.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* @ignore
-*/
-if (!defined('IN_PHPBB'))
-{
- exit;
-}
-
-use Symfony\Component\Config\FileLocator;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
-
-/**
-* Load the service configurations from all extensions into the container.
-*/
-class phpbb_di_processor_ext implements phpbb_di_processor_interface
-{
- private $extension_manager;
-
- /**
- * Constructor.
- *
- * @param string $extension_manager The extension manager
- */
- public function __construct($extension_manager)
- {
- $this->extension_manager = $extension_manager;
- }
-
- /**
- * @inheritdoc
- */
- public function process(ContainerBuilder $container)
- {
- $enabled_exts = $this->extension_manager->all_enabled();
- foreach ($enabled_exts as $name => $path)
- {
- if (file_exists($path . '/config/services.yml'))
- {
- $loader = new YamlFileLoader($container, new FileLocator($path . '/config'));
- $loader->load('services.yml');
- }
- }
- }
-}
diff --git a/phpBB/includes/di/processor/interface.php b/phpBB/includes/di/processor/interface.php
deleted file mode 100644
index b8563791cc..0000000000
--- a/phpBB/includes/di/processor/interface.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* @ignore
-*/
-if (!defined('IN_PHPBB'))
-{
- exit;
-}
-
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-interface phpbb_di_processor_interface
-{
- /**
- * Mutate the container.
- *
- * @param ContainerBuilder $container The container
- */
- public function process(ContainerBuilder $container);
-}
diff --git a/phpBB/includes/di/service_collection.php b/phpBB/includes/di/service_collection.php
new file mode 100644
index 0000000000..880cb46d4d
--- /dev/null
+++ b/phpBB/includes/di/service_collection.php
@@ -0,0 +1,49 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+* Collection of services to be configured at container compile time.
+*
+* @package phpBB3
+*/
+class phpbb_di_service_collection extends ArrayObject
+{
+ /**
+ * Constructor
+ *
+ * @param ContainerInterface $container Container object
+ */
+ public function __construct(ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
+ /**
+ * Add a service to the collection
+ *
+ * @param string $name The service name
+ * @return null
+ */
+ public function add($name)
+ {
+ $task = $this->container->get($name);
+
+ $this->offsetSet($name, $task);
+ }
+}
diff --git a/phpBB/includes/event/dispatcher.php b/phpBB/includes/event/dispatcher.php
index 2bf46b9b06..4f637ce3bb 100644
--- a/phpBB/includes/event/dispatcher.php
+++ b/phpBB/includes/event/dispatcher.php
@@ -15,7 +15,7 @@ if (!defined('IN_PHPBB'))
exit;
}
-use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
/**
* Extension of the Symfony2 EventDispatcher
@@ -31,7 +31,7 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
* extract($phpbb_dispatcher->trigger_event('core.index', compact($vars)));
*
*/
-class phpbb_event_dispatcher extends EventDispatcher
+class phpbb_event_dispatcher extends ContainerAwareEventDispatcher
{
public function trigger_event($eventName, $data = array())
{
diff --git a/phpBB/includes/event/kernel_exception_subscriber.php b/phpBB/includes/event/kernel_exception_subscriber.php
new file mode 100644
index 0000000000..f90989a74c
--- /dev/null
+++ b/phpBB/includes/event/kernel_exception_subscriber.php
@@ -0,0 +1,85 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+use Symfony\Component\HttpFoundation\Response;
+
+class phpbb_event_kernel_exception_subscriber implements EventSubscriberInterface
+{
+ /**
+ * Template object
+ * @var phpbb_template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var phpbb_user
+ */
+ protected $user;
+
+ /**
+ * Construct method
+ *
+ * @param phpbb_template $template Template object
+ * @param phpbb_user $user User object
+ */
+ public function __construct(phpbb_template $template, phpbb_user $user)
+ {
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * This listener is run when the KernelEvents::EXCEPTION event is triggered
+ *
+ * @param GetResponseForExceptionEvent $event
+ * @return null
+ */
+ public function on_kernel_exception(GetResponseForExceptionEvent $event)
+ {
+ page_header($this->user->lang('INFORMATION'));
+
+ $exception = $event->getException();
+
+ $this->template->assign_vars(array(
+ 'MESSAGE_TITLE' => $this->user->lang('INFORMATION'),
+ 'MESSAGE_TEXT' => $exception->getMessage(),
+ ));
+
+ $this->template->set_filenames(array(
+ 'body' => 'message_body.html',
+ ));
+
+ page_footer(true, false, false);
+
+
+ $status_code = $exception instanceof HttpException ? $exception->getStatusCode() : 500;
+ $response = new Response($this->template->assign_display('body'), $status_code);
+ $event->setResponse($response);
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ KernelEvents::EXCEPTION => 'on_kernel_exception',
+ );
+ }
+}
diff --git a/phpBB/includes/event/kernel_request_subscriber.php b/phpBB/includes/event/kernel_request_subscriber.php
new file mode 100644
index 0000000000..afb8464f80
--- /dev/null
+++ b/phpBB/includes/event/kernel_request_subscriber.php
@@ -0,0 +1,83 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\EventListener\RouterListener;
+use Symfony\Component\Routing\RequestContext;
+
+class phpbb_event_kernel_request_subscriber implements EventSubscriberInterface
+{
+ /**
+ * Extension finder object
+ * @var phpbb_extension_finder
+ */
+ protected $finder;
+
+ /**
+ * PHP extension
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Root path
+ * @var string
+ */
+ protected $root_path;
+
+ /**
+ * Construct method
+ *
+ * @param phpbb_extension_finder $finder Extension finder object
+ * @param string $root_path Root path
+ * @param string $php_ext PHP extension
+ */
+ public function __construct(phpbb_extension_finder $finder, $root_path, $php_ext)
+ {
+ $this->finder = $finder;
+ $this->root_path = $root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * This listener is run when the KernelEvents::REQUEST event is triggered
+ *
+ * This is responsible for setting up the routing information
+ *
+ * @param GetResponseEvent $event
+ * @return null
+ */
+ public function on_kernel_request(GetResponseEvent $event)
+ {
+ $request = $event->getRequest();
+ $context = new RequestContext();
+ $context->fromRequest($request);
+
+ $matcher = phpbb_get_url_matcher($this->finder, $context, $this->root_path, $this->php_ext);
+ $router_listener = new RouterListener($matcher, $context);
+ $router_listener->onKernelRequest($event);
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ KernelEvents::REQUEST => 'on_kernel_request',
+ );
+ }
+}
diff --git a/phpBB/includes/event/kernel_terminate_subscriber.php b/phpBB/includes/event/kernel_terminate_subscriber.php
new file mode 100644
index 0000000000..1eaf890e42
--- /dev/null
+++ b/phpBB/includes/event/kernel_terminate_subscriber.php
@@ -0,0 +1,43 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\PostResponseEvent;
+
+class phpbb_event_kernel_terminate_subscriber implements EventSubscriberInterface
+{
+ /**
+ * This listener is run when the KernelEvents::TERMINATE event is triggered
+ * This comes after a Response has been sent to the server; this is
+ * primarily cleanup stuff.
+ *
+ * @param PostResponseEvent $event
+ * @return null
+ */
+ public function on_kernel_terminate(PostResponseEvent $event)
+ {
+ exit_handler();
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ KernelEvents::TERMINATE => 'on_kernel_terminate',
+ );
+ }
+}
diff --git a/phpBB/includes/extension/controller.php b/phpBB/includes/extension/controller.php
deleted file mode 100644
index f97b69c7ed..0000000000
--- a/phpBB/includes/extension/controller.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
-*
-* @package extension
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* @ignore
-*/
-if (!defined('IN_PHPBB'))
-{
- exit;
-}
-
-/**
-* Abstract class extended by extension front controller classes
-*
-* @package extension
-*/
-abstract class phpbb_extension_controller implements phpbb_extension_controller_interface
-{
- /**
- * Request class object
- * @var phpbb_request
- */
- protected $request;
-
- /**
- * DBAL class object
- * @var dbal
- */
- protected $db;
-
- /**
- * User class object
- * @var phpbb_user
- */
- protected $user;
-
- /**
- * Template class object
- * @var phpbb_template
- */
- protected $template;
-
- /**
- * Config object
- * @var phpbb_config
- */
- protected $config;
-
- /**
- * PHP Extension
- * @var string
- */
- protected $php_ext;
-
- /**
- * Relative path to board root
- * @var string
- */
- protected $phpbb_root_path;
-
- /**
- * Constructor method that provides the common phpBB objects as inherited class
- * properties for automatic availability in extension controllers
- */
- public function __construct()
- {
- global $request, $db, $user, $template, $config;
- global $phpEx, $phpbb_root_path;
-
- $this->request = $request;
- $this->db = $db;
- $this->user = $user;
- $this->template = $template;
- $this->config = $config;
- $this->php_ext = $phpEx;
- $this->phpbb_root_path = $phpbb_root_path;
- }
-}
diff --git a/phpBB/includes/extension/controller_interface.php b/phpBB/includes/extension/controller_interface.php
deleted file mode 100644
index 2b88925388..0000000000
--- a/phpBB/includes/extension/controller_interface.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-/**
-*
-* @package extension
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* @ignore
-*/
-if (!defined('IN_PHPBB'))
-{
- exit;
-}
-
-/**
-* The interface that extension classes have to implement to run front pages
-*
-* @package extension
-*/
-interface phpbb_extension_controller_interface
-{
- /**
- * Handle the request to display a page from an extension
- *
- * @return null
- */
- public function handle();
-}
diff --git a/phpBB/includes/extension/interface.php b/phpBB/includes/extension/interface.php
index 74ecb9b762..7b36a12bf6 100644
--- a/phpBB/includes/extension/interface.php
+++ b/phpBB/includes/extension/interface.php
@@ -45,7 +45,9 @@ interface phpbb_extension_interface
*
* @param mixed $old_state The return value of the previous call
* of this method, or false on the first call
- * @return null
+ * @return mixed Returns false after last step, otherwise
+ * temporary state which is passed as an
+ * argument to the next step
*/
public function disable_step($old_state);
diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php
index cfa6a0e000..de6f364320 100644
--- a/phpBB/includes/extension/manager.php
+++ b/phpBB/includes/extension/manager.php
@@ -34,7 +34,7 @@ class phpbb_extension_manager
/**
* Creates a manager and loads information from database
*
- * @param dbal $db A database connection
+ * @param phpbb_db_driver $db A database connection
* @param phpbb_config $config phpbb_config
* @param string $extension_table The name of the table holding extensions
* @param string $phpbb_root_path Path to the phpbb includes directory.
@@ -42,7 +42,7 @@ class phpbb_extension_manager
* @param phpbb_cache_driver_interface $cache A cache instance or null
* @param string $cache_name The name of the cache variable, defaults to _ext
*/
- public function __construct(dbal $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
+ public function __construct(phpbb_db_driver $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
{
$this->phpbb_root_path = $phpbb_root_path;
$this->db = $db;
@@ -195,7 +195,7 @@ class phpbb_extension_manager
if ($this->cache)
{
- $this->cache->destroy($this->cache_name);
+ $this->cache->purge();
}
return !$active;
@@ -252,7 +252,7 @@ class phpbb_extension_manager
if ($this->cache)
{
- $this->cache->destroy($this->cache_name);
+ $this->cache->purge();
}
return true;
@@ -272,7 +272,7 @@ class phpbb_extension_manager
if ($this->cache)
{
- $this->cache->destroy($this->cache_name);
+ $this->cache->purge();
}
return false;
@@ -335,7 +335,7 @@ class phpbb_extension_manager
if ($this->cache)
{
- $this->cache->destroy($this->cache_name);
+ $this->cache->purge();
}
return true;
@@ -349,7 +349,7 @@ class phpbb_extension_manager
if ($this->cache)
{
- $this->cache->destroy($this->cache_name);
+ $this->cache->purge();
}
return false;
@@ -384,7 +384,7 @@ class phpbb_extension_manager
}
$iterator = new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/'),
+ new RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/', FilesystemIterator::NEW_CURRENT_AND_KEY | FilesystemIterator::FOLLOW_SYMLINKS),
RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $file_info)
{
diff --git a/phpBB/includes/extension/metadata_manager.php b/phpBB/includes/extension/metadata_manager.php
index ea85bd3c4e..36b0f8b184 100644
--- a/phpBB/includes/extension/metadata_manager.php
+++ b/phpBB/includes/extension/metadata_manager.php
@@ -34,12 +34,12 @@ class phpbb_extension_metadata_manager
/**
* Creates the metadata manager
*
- * @param dbal $db A database connection
+ * @param phpbb_db_driver $db A database connection
* @param string $extension_manager An instance of the phpbb extension manager
* @param string $phpbb_root_path Path to the phpbb includes directory.
* @param string $phpEx php file extension
*/
- public function __construct($ext_name, dbal $db, phpbb_extension_manager $extension_manager, $phpbb_root_path, $phpEx = '.php', phpbb_template $template, phpbb_config $config)
+ public function __construct($ext_name, phpbb_db_driver $db, phpbb_extension_manager $extension_manager, $phpbb_root_path, $phpEx = '.php', phpbb_template $template, phpbb_config $config)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->db = $db;
diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php
index 804d89d1a2..d0ef2759d5 100644
--- a/phpBB/includes/functions.php
+++ b/phpBB/includes/functions.php
@@ -7,6 +7,8 @@
*
*/
+use Symfony\Component\HttpFoundation\Request;
+
/**
* @ignore
*/
@@ -229,7 +231,8 @@ function phpbb_gmgetdate($time = false)
/**
* Return formatted string for filesizes
*
-* @param int $value filesize in bytes
+* @param mixed $value filesize in bytes
+* (non-negative number; int, float or string)
* @param bool $string_only true if language string should be returned
* @param array $allowed_units only allow these units (data array indexes)
*
@@ -241,6 +244,12 @@ function get_formatted_filesize($value, $string_only = true, $allowed_units = fa
global $user;
$available_units = array(
+ 'tb' => array(
+ 'min' => 1099511627776, // pow(2, 40)
+ 'index' => 4,
+ 'si_unit' => 'TB',
+ 'iec_unit' => 'TIB',
+ ),
'gb' => array(
'min' => 1073741824, // pow(2, 30)
'index' => 3,
@@ -2847,12 +2856,10 @@ function meta_refresh($time, $url, $disable_cd_check = false)
*
* @param int $code HTTP status code
* @param string $message Message for the status code
-* @return void
+* @return null
*/
function send_status_line($code, $message)
{
- global $request;
-
if (substr(strtolower(@php_sapi_name()), 0, 3) === 'cgi')
{
// in theory, we shouldn't need that due to php doing it. Reality offers a differing opinion, though
@@ -2860,18 +2867,35 @@ function send_status_line($code, $message)
}
else
{
- if ($request->server('SERVER_PROTOCOL'))
- {
- $version = $request->server('SERVER_PROTOCOL');
- }
- else
- {
- $version = 'HTTP/1.0';
- }
+ $version = phpbb_request_http_version();
header("$version $code $message", true, $code);
}
}
+/**
+* Returns the HTTP version used in the current request.
+*
+* Handles the case of being called before $request is present,
+* in which case it falls back to the $_SERVER superglobal.
+*
+* @return string HTTP version
+*/
+function phpbb_request_http_version()
+{
+ global $request;
+
+ if ($request && $request->server('SERVER_PROTOCOL'))
+ {
+ return $request->server('SERVER_PROTOCOL');
+ }
+ else if (isset($_SERVER['SERVER_PROTOCOL']))
+ {
+ return $_SERVER['SERVER_PROTOCOL'];
+ }
+
+ return 'HTTP/1.0';
+}
+
//Form validation
@@ -4771,7 +4795,7 @@ function phpbb_get_plural_form($rule, $number)
*
* @param array $param Parameter array, see $param_defaults array.
*
-* @return void
+* @return null
*/
function phpbb_http_login($param)
{
@@ -4870,12 +4894,107 @@ function phpbb_http_login($param)
}
/**
+* Escapes and quotes a string for use as an HTML/XML attribute value.
+*
+* This is a port of Python xml.sax.saxutils quoteattr.
+*
+* The function will attempt to choose a quote character in such a way as to
+* avoid escaping quotes in the string. If this is not possible the string will
+* be wrapped in double quotes and double quotes will be escaped.
+*
+* @param string $data The string to be escaped
+* @param array $entities Associative array of additional entities to be escaped
+* @return string Escaped and quoted string
+*/
+function phpbb_quoteattr($data, $entities = null)
+{
+ $data = str_replace('&', '&amp;', $data);
+ $data = str_replace('>', '&gt;', $data);
+ $data = str_replace('<', '&lt;', $data);
+
+ $data = str_replace("\n", '&#10;', $data);
+ $data = str_replace("\r", '&#13;', $data);
+ $data = str_replace("\t", '&#9;', $data);
+
+ if (!empty($entities))
+ {
+ $data = str_replace(array_keys($entities), array_values($entities), $data);
+ }
+
+ if (strpos($data, '"') !== false)
+ {
+ if (strpos($data, "'") !== false)
+ {
+ $data = '"' . str_replace('"', '&quot;', $data) . '"';
+ }
+ else
+ {
+ $data = "'" . $data . "'";
+ }
+ }
+ else
+ {
+ $data = '"' . $data . '"';
+ }
+
+ return $data;
+}
+
+/**
+* Converts query string (GET) parameters in request into hidden fields.
+*
+* Useful for forwarding GET parameters when submitting forms with GET method.
+*
+* It is possible to omit some of the GET parameters, which is useful if
+* they are specified in the form being submitted.
+*
+* sid is always omitted.
+*
+* @param phpbb_request $request Request object
+* @param array $exclude A list of variable names that should not be forwarded
+* @return string HTML with hidden fields
+*/
+function phpbb_build_hidden_fields_for_query_params($request, $exclude = null)
+{
+ $names = $request->variable_names(phpbb_request_interface::GET);
+ $hidden = '';
+ foreach ($names as $name)
+ {
+ // Sessions are dealt with elsewhere, omit sid always
+ if ($name == 'sid')
+ {
+ continue;
+ }
+
+ // Omit any additional parameters requested
+ if (!empty($exclude) && in_array($name, $exclude))
+ {
+ continue;
+ }
+
+ $escaped_name = phpbb_quoteattr($name);
+
+ // Note: we might retrieve the variable from POST or cookies
+ // here. To avoid exposing cookies, skip variables that are
+ // overwritten somewhere other than GET entirely.
+ $value = $request->variable($name, '', true);
+ $get_value = $request->variable($name, '', true, phpbb_request_interface::GET);
+ if ($value === $get_value)
+ {
+ $escaped_value = phpbb_quoteattr($value);
+ $hidden .= "<input type='hidden' name=$escaped_name value=$escaped_value />";
+ }
+ }
+ return $hidden;
+}
+
+/**
* Generate page header
*/
function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum')
{
global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path;
- global $phpbb_dispatcher;
+ global $phpbb_dispatcher, $request;
if (defined('HEADER_INC'))
{
@@ -5064,6 +5183,8 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
$timezone_name = $user->lang['timezones'][$timezone_name];
}
+ $hidden_fields_for_jumpbox = phpbb_build_hidden_fields_for_query_params($request, array('f'));
+
// The following assigns all _common_ variables that may be used at any point in a template.
$template->assign_vars(array(
'SITENAME' => $config['sitename'],
@@ -5078,6 +5199,7 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
'RECORD_USERS' => $l_online_record,
'PRIVATE_MESSAGE_INFO' => $l_privmsgs_text,
'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread,
+ 'HIDDEN_FIELDS_FOR_JUMPBOX' => $hidden_fields_for_jumpbox,
'S_USER_NEW_PRIVMSG' => $user->data['user_new_privmsg'],
'S_USER_UNREAD_PRIVMSG' => $user->data['user_unread_privmsg'],
@@ -5209,11 +5331,15 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
/**
* Generate page footer
+*
+* @param bool $run_cron Whether or not to run the cron
+* @param bool $display_template Whether or not to display the template
+* @param bool $exit_handler Whether or not to run the exit_handler()
*/
-function page_footer($run_cron = true)
+function page_footer($run_cron = true, $display_template = true, $exit_handler = true)
{
global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx;
- global $request, $phpbb_dispatcher;
+ global $request, $phpbb_dispatcher, $phpbb_admin_path;
// A listener can set this variable to `true` when it overrides this function
$page_footer_override = false;
@@ -5269,7 +5395,7 @@ function page_footer($run_cron = true)
'TRANSLATION_INFO' => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '',
'CREDIT_LINE' => $user->lang('POWERED_BY', '<a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Group'),
- 'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '')
+ 'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_admin_path}index.$phpEx", false, true, $user->session_id) : '')
);
// Call cron-type script
@@ -5305,10 +5431,17 @@ function page_footer($run_cron = true)
}
}
- $template->display('body');
+ if ($display_template)
+ {
+ $template->display('body');
+ }
garbage_collection();
- exit_handler();
+
+ if ($exit_handler)
+ {
+ exit_handler();
+ }
}
/**
@@ -5326,7 +5459,10 @@ function garbage_collection()
* @event core.garbage_collection
* @since 3.1-A1
*/
- $phpbb_dispatcher->dispatch('core.garbage_collection');
+ if (!empty($phpbb_dispatcher))
+ {
+ $phpbb_dispatcher->dispatch('core.garbage_collection');
+ }
// Unload cache, must be done before the DB connection if closed
if (!empty($cache))
@@ -5412,3 +5548,95 @@ function phpbb_to_numeric($input)
{
return ($input > PHP_INT_MAX) ? (float) $input : (int) $input;
}
+
+/**
+* Convert either 3.0 dbms or 3.1 db driver class name to 3.1 db driver class name.
+*
+* If $dbms is a valid 3.1 db driver class name, returns it unchanged.
+* Otherwise prepends phpbb_db_driver_ to the dbms to convert a 3.0 dbms
+* to 3.1 db driver class name.
+*
+* @param string $dbms dbms parameter
+* @return db driver class
+*/
+function phpbb_convert_30_dbms_to_31($dbms)
+{
+ // Note: this check is done first because mysqli extension
+ // supplies a mysqli class, and class_exists($dbms) would return
+ // true for mysqli class.
+ // However, per the docblock any valid 3.1 driver name should be
+ // recognized by this function, and have priority over 3.0 dbms.
+ if (class_exists('phpbb_db_driver_' . $dbms))
+ {
+ return 'phpbb_db_driver_' . $dbms;
+ }
+
+ if (class_exists($dbms))
+ {
+ // Additionally we could check that $dbms extends phpbb_db_driver.
+ // http://php.net/manual/en/class.reflectionclass.php
+ // Beware of possible performance issues:
+ // http://stackoverflow.com/questions/294582/php-5-reflection-api-performance
+ // We could check for interface implementation in all paths or
+ // only when we do not prepend phpbb_db_driver_.
+
+ /*
+ $reflection = new \ReflectionClass($dbms);
+
+ if ($reflection->isSubclassOf('phpbb_db_driver'))
+ {
+ return $dbms;
+ }
+ */
+
+ return $dbms;
+ }
+
+ throw new \RuntimeException("You have specified an invalid dbms driver: $dbms");
+}
+
+/**
+* Create a Symfony Request object from phpbb_request object
+*
+* @param phpbb_request $request Request object
+* @return Request A Symfony Request object
+*/
+function phpbb_create_symfony_request(phpbb_request $request)
+{
+ // This function is meant to sanitize the global input arrays
+ $sanitizer = function(&$value, $key) {
+ $type_cast_helper = new phpbb_request_type_cast_helper();
+ $type_cast_helper->set_var($value, $value, gettype($value), true);
+ };
+
+ // We need to re-enable the super globals so we can access them here
+ $request->enable_super_globals();
+ $get_parameters = $_GET;
+ $post_parameters = $_POST;
+ $server_parameters = $_SERVER;
+ $files_parameters = $_FILES;
+ $cookie_parameters = $_COOKIE;
+ // And now disable them again for security
+ $request->disable_super_globals();
+
+ array_walk_recursive($get_parameters, $sanitizer);
+ array_walk_recursive($post_parameters, $sanitizer);
+
+ // Until we fix the issue with relative paths, we have to fake path info
+ // to allow urls like app.php?controller=foo/bar
+ $controller = $request->variable('controller', '');
+ $path_info = '/' . $controller;
+ $request_uri = $server_parameters['REQUEST_URI'];
+
+ // Remove the query string from REQUEST_URI
+ if ($pos = strpos($request_uri, '?'))
+ {
+ $request_uri = substr($request_uri, 0, $pos);
+ }
+
+ // Add the path info (i.e. controller route) to the REQUEST_URI
+ $server_parameters['REQUEST_URI'] = $request_uri . $path_info;
+ $server_parameters['SCRIPT_NAME'] = '';
+
+ return new Request($get_parameters, $post_parameters, array(), $cookie_parameters, $files_parameters, $server_parameters);
+}
diff --git a/phpBB/includes/functions_acp.php b/phpBB/includes/functions_acp.php
index 2f3fd7bac0..32fd76e74d 100644
--- a/phpBB/includes/functions_acp.php
+++ b/phpBB/includes/functions_acp.php
@@ -82,16 +82,16 @@ function adm_page_header($page_title)
'T_RANKS_PATH' => "{$phpbb_root_path}{$config['ranks_path']}/",
'T_UPLOAD_PATH' => "{$phpbb_root_path}{$config['upload_path']}/",
- 'ICON_MOVE_UP' => '<img src="' . $phpbb_admin_path . 'images/icon_up.gif" alt="' . $user->lang['MOVE_UP'] . '" title="' . $user->lang['MOVE_UP'] . '" />',
- 'ICON_MOVE_UP_DISABLED' => '<img src="' . $phpbb_admin_path . 'images/icon_up_disabled.gif" alt="' . $user->lang['MOVE_UP'] . '" title="' . $user->lang['MOVE_UP'] . '" />',
- 'ICON_MOVE_DOWN' => '<img src="' . $phpbb_admin_path . 'images/icon_down.gif" alt="' . $user->lang['MOVE_DOWN'] . '" title="' . $user->lang['MOVE_DOWN'] . '" />',
- 'ICON_MOVE_DOWN_DISABLED' => '<img src="' . $phpbb_admin_path . 'images/icon_down_disabled.gif" alt="' . $user->lang['MOVE_DOWN'] . '" title="' . $user->lang['MOVE_DOWN'] . '" />',
- 'ICON_EDIT' => '<img src="' . $phpbb_admin_path . 'images/icon_edit.gif" alt="' . $user->lang['EDIT'] . '" title="' . $user->lang['EDIT'] . '" />',
- 'ICON_EDIT_DISABLED' => '<img src="' . $phpbb_admin_path . 'images/icon_edit_disabled.gif" alt="' . $user->lang['EDIT'] . '" title="' . $user->lang['EDIT'] . '" />',
- 'ICON_DELETE' => '<img src="' . $phpbb_admin_path . 'images/icon_delete.gif" alt="' . $user->lang['DELETE'] . '" title="' . $user->lang['DELETE'] . '" />',
- 'ICON_DELETE_DISABLED' => '<img src="' . $phpbb_admin_path . 'images/icon_delete_disabled.gif" alt="' . $user->lang['DELETE'] . '" title="' . $user->lang['DELETE'] . '" />',
- 'ICON_SYNC' => '<img src="' . $phpbb_admin_path . 'images/icon_sync.gif" alt="' . $user->lang['RESYNC'] . '" title="' . $user->lang['RESYNC'] . '" />',
- 'ICON_SYNC_DISABLED' => '<img src="' . $phpbb_admin_path . 'images/icon_sync_disabled.gif" alt="' . $user->lang['RESYNC'] . '" title="' . $user->lang['RESYNC'] . '" />',
+ 'ICON_MOVE_UP' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_up.gif" alt="' . $user->lang['MOVE_UP'] . '" title="' . $user->lang['MOVE_UP'] . '" />',
+ 'ICON_MOVE_UP_DISABLED' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_up_disabled.gif" alt="' . $user->lang['MOVE_UP'] . '" title="' . $user->lang['MOVE_UP'] . '" />',
+ 'ICON_MOVE_DOWN' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_down.gif" alt="' . $user->lang['MOVE_DOWN'] . '" title="' . $user->lang['MOVE_DOWN'] . '" />',
+ 'ICON_MOVE_DOWN_DISABLED' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_down_disabled.gif" alt="' . $user->lang['MOVE_DOWN'] . '" title="' . $user->lang['MOVE_DOWN'] . '" />',
+ 'ICON_EDIT' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_edit.gif" alt="' . $user->lang['EDIT'] . '" title="' . $user->lang['EDIT'] . '" />',
+ 'ICON_EDIT_DISABLED' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_edit_disabled.gif" alt="' . $user->lang['EDIT'] . '" title="' . $user->lang['EDIT'] . '" />',
+ 'ICON_DELETE' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_delete.gif" alt="' . $user->lang['DELETE'] . '" title="' . $user->lang['DELETE'] . '" />',
+ 'ICON_DELETE_DISABLED' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_delete_disabled.gif" alt="' . $user->lang['DELETE'] . '" title="' . $user->lang['DELETE'] . '" />',
+ 'ICON_SYNC' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_sync.gif" alt="' . $user->lang['RESYNC'] . '" title="' . $user->lang['RESYNC'] . '" />',
+ 'ICON_SYNC_DISABLED' => '<img src="' . htmlspecialchars($phpbb_admin_path) . 'images/icon_sync_disabled.gif" alt="' . $user->lang['RESYNC'] . '" title="' . $user->lang['RESYNC'] . '" />',
'S_USER_LANG' => $user->lang['USER_LANG'],
'S_CONTENT_DIRECTION' => $user->lang['DIRECTION'],
diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php
index 5e2ee8c8f6..5529f2af46 100644
--- a/phpBB/includes/functions_admin.php
+++ b/phpBB/includes/functions_admin.php
@@ -2292,13 +2292,17 @@ function auto_prune($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_fr
}
/**
-* Cache moderators, called whenever permissions are changed via admin_permissions. Changes of username
-* and group names must be carried through for the moderators table
+* Cache moderators. Called whenever permissions are changed
+* via admin_permissions. Changes of usernames and group names
+* must be carried through for the moderators table.
+*
+* @param phpbb_db_driver $db Database connection
+* @param phpbb_cache_driver_interface Cache driver
+* @param phpbb_auth $auth Authentication object
+* @return null
*/
-function cache_moderators()
+function phpbb_cache_moderators($db, $cache, $auth)
{
- global $db, $cache, $auth, $phpbb_root_path, $phpEx;
-
// Remove cached sql results
$cache->destroy('sql', MODERATOR_CACHE_TABLE);
@@ -2469,6 +2473,20 @@ function cache_moderators()
}
/**
+* Cache moderators. Called whenever permissions are changed
+* via admin_permissions. Changes of usernames and group names
+* must be carried through for the moderators table.
+*
+* @deprecated 3.1
+* @return null
+*/
+function cache_moderators()
+{
+ global $db, $cache, $auth;
+ return phpbb_cache_moderators($db, $cache, $auth);
+}
+
+/**
* View log
* If $log_count is set to false, we will skip counting all entries in the database.
*/
@@ -2740,12 +2758,16 @@ function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id
}
/**
-* Update foes - remove moderators and administrators from foe lists...
+* Removes moderators and administrators from foe lists.
+*
+* @param phpbb_db_driver $db Database connection
+* @param phpbb_auth $auth Authentication object
+* @param array|bool $group_id If an array, remove all members of this group from foe lists, or false to ignore
+* @param array|bool $user_id If an array, remove this user from foe lists, or false to ignore
+* @return null
*/
-function update_foes($group_id = false, $user_id = false)
+function phpbb_update_foes($db, $auth, $group_id = false, $user_id = false)
{
- global $db, $auth;
-
// update foes for some user
if (is_array($user_id) && sizeof($user_id))
{
@@ -2855,6 +2877,20 @@ function update_foes($group_id = false, $user_id = false)
}
/**
+* Removes moderators and administrators from foe lists.
+*
+* @deprecated 3.1
+* @param array|bool $group_id If an array, remove all members of this group from foe lists, or false to ignore
+* @param array|bool $user_id If an array, remove this user from foe lists, or false to ignore
+* @return null
+*/
+function update_foes($group_id = false, $user_id = false)
+{
+ global $db, $auth;
+ return phpbb_update_foes($db, $auth, $group_id, $user_id);
+}
+
+/**
* Lists inactive users
*/
function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_inactive_time DESC')
@@ -3308,7 +3344,7 @@ function obtain_latest_version_info($force_update = false, $warn_fail = false, $
* @param int $flag The binary flag which is OR-ed with the current column value
* @param string $sql_more This string is attached to the sql query generated to update the table.
*
- * @return void
+ * @return null
*/
function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '')
{
diff --git a/phpBB/includes/functions_container.php b/phpBB/includes/functions_container.php
new file mode 100644
index 0000000000..a3ed21c35b
--- /dev/null
+++ b/phpBB/includes/functions_container.php
@@ -0,0 +1,199 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Create the ContainerBuilder object
+*
+* @param array $extensions Array of Container extension objects
+* @param string $phpbb_root_path Root path
+* @param string $php_ext PHP Extension
+* @return ContainerBuilder object
+*/
+function phpbb_create_container(array $extensions, $phpbb_root_path, $php_ext)
+{
+ $container = new ContainerBuilder();
+
+ foreach ($extensions as $extension)
+ {
+ $container->registerExtension($extension);
+ $container->loadFromExtension($extension->getAlias());
+ }
+
+ $container->setParameter('core.root_path', $phpbb_root_path);
+ $container->setParameter('core.php_ext', $php_ext);
+
+ return $container;
+}
+
+/**
+* Create installer container
+*
+* @param string $phpbb_root_path Root path
+* @param string $php_ext PHP Extension
+* @return ContainerBuilder object
+*/
+function phpbb_create_install_container($phpbb_root_path, $php_ext)
+{
+ $core = new phpbb_di_extension_core($phpbb_root_path);
+ $container = phpbb_create_container(array($core), $phpbb_root_path, $php_ext);
+
+ $container->setParameter('core.root_path', $phpbb_root_path);
+ $container->setParameter('core.php_ext', $php_ext);
+ $container->setParameter('core.table_prefix', '');
+
+ $container->register('dbal.conn')->setSynthetic(true);
+
+ $container->setAlias('cache.driver', 'cache.driver.install');
+
+ $container->compile();
+
+ return $container;
+}
+
+/**
+* Create a compiled ContainerBuilder object
+*
+* @param array $extensions Array of Container extension objects
+* @param array $passes Array of Compiler Pass objects
+* @param string $phpbb_root_path Root path
+* @param string $php_ext PHP Extension
+* @return ContainerBuilder object (compiled)
+*/
+function phpbb_create_compiled_container(array $extensions, array $passes, $phpbb_root_path, $php_ext)
+{
+ // Create a temporary container for access to the ext.manager service
+ $tmp_container = phpbb_create_container($extensions, $phpbb_root_path, $php_ext);
+ $tmp_container->compile();
+
+ // XXX stop writing to global $cache when
+ // http://tracker.phpbb.com/browse/PHPBB3-11203 is fixed
+ $GLOBALS['cache'] = $tmp_container->get('cache');
+ $installed_exts = $tmp_container->get('ext.manager')->all_enabled();
+
+ // Now pass the enabled extension paths into the ext compiler extension
+ $extensions[] = new phpbb_di_extension_ext($installed_exts);
+
+ // Create the final container to be compiled and cached
+ $container = phpbb_create_container($extensions, $phpbb_root_path, $php_ext);
+
+ // Compile the container
+ foreach ($passes as $pass)
+ {
+ $container->addCompilerPass($pass);
+ }
+ $container->compile();
+
+ return $container;
+}
+
+/**
+* Create a compiled and dumped ContainerBuilder object
+*
+* @param array $extensions Array of Container extension objects
+* @param array $passes Array of Compiler Pass objects
+* @param string $phpbb_root_path Root path
+* @param string $php_ext PHP Extension
+* @return ContainerBuilder object (compiled)
+*/
+function phpbb_create_dumped_container(array $extensions, array $passes, $phpbb_root_path, $php_ext)
+{
+ // Check for our cached container; if it exists, use it
+ $container_filename = phpbb_container_filename($phpbb_root_path, $php_ext);
+ if (file_exists($container_filename))
+ {
+ require($container_filename);
+ return new phpbb_cache_container();
+ }
+
+ $container = phpbb_create_compiled_container($extensions, $passes, $phpbb_root_path, $php_ext);
+
+ // Lastly, we create our cached container class
+ $dumper = new PhpDumper($container);
+ $cached_container_dump = $dumper->dump(array(
+ 'class' => 'phpbb_cache_container',
+ 'base_class' => 'Symfony\\Component\\DependencyInjection\\ContainerBuilder',
+ ));
+
+ file_put_contents($container_filename, $cached_container_dump);
+
+ return $container;
+}
+
+/**
+* Create an environment-specific ContainerBuilder object
+*
+* If debug is enabled, the container is re-compiled every time.
+* This ensures that the latest changes will always be reflected
+* during development.
+*
+* Otherwise it will get the existing dumped container and use
+* that one instead.
+*
+* @param array $extensions Array of Container extension objects
+* @param array $passes Array of Compiler Pass objects
+* @param string $phpbb_root_path Root path
+* @param string $php_ext PHP Extension
+* @return ContainerBuilder object (compiled)
+*/
+function phpbb_create_dumped_container_unless_debug(array $extensions, array $passes, $phpbb_root_path, $php_ext)
+{
+ $container_factory = defined('DEBUG') ? 'phpbb_create_compiled_container' : 'phpbb_create_dumped_container';
+ return $container_factory($extensions, $passes, $phpbb_root_path, $php_ext);
+}
+
+/**
+* Create a default ContainerBuilder object
+*
+* Contains the default configuration of the phpBB container.
+*
+* @param array $extensions Array of Container extension objects
+* @param array $passes Array of Compiler Pass objects
+* @return ContainerBuilder object (compiled)
+*/
+function phpbb_create_default_container($phpbb_root_path, $php_ext)
+{
+ return phpbb_create_dumped_container_unless_debug(
+ array(
+ new phpbb_di_extension_config($phpbb_root_path . 'config.' . $php_ext),
+ new phpbb_di_extension_core($phpbb_root_path),
+ ),
+ array(
+ new phpbb_di_pass_collection_pass(),
+ new phpbb_di_pass_kernel_pass(),
+ ),
+ $phpbb_root_path,
+ $php_ext
+ );
+}
+
+/**
+* Get the filename under which the dumped container will be stored.
+*
+* @param string $phpbb_root_path Root path
+* @param string $php_ext PHP Extension
+* @return Path for dumped container
+*/
+function phpbb_container_filename($phpbb_root_path, $php_ext)
+{
+ $filename = str_replace(array('/', '.'), array('slash', 'dot'), $phpbb_root_path);
+ return $phpbb_root_path . 'cache/container_' . $filename . '.' . $php_ext;
+}
diff --git a/phpBB/includes/functions_database_helper.php b/phpBB/includes/functions_database_helper.php
new file mode 100644
index 0000000000..1b508e6a02
--- /dev/null
+++ b/phpBB/includes/functions_database_helper.php
@@ -0,0 +1,206 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Updates rows in given table from a set of values to a new value.
+* If this results in rows violating uniqueness constraints, the duplicate
+* rows are eliminated.
+*
+* The only supported table is bookmarks.
+*
+* @param phpbb_db_driver $db Database object
+* @param string $table Table on which to perform the update
+* @param string $column Column whose values to change
+* @param array $from_values An array of values that should be changed
+* @param int $to_value The new value
+* @return null
+*/
+function phpbb_update_rows_avoiding_duplicates(phpbb_db_driver $db, $table, $column, $from_values, $to_value)
+{
+ $sql = "SELECT $column, user_id
+ FROM $table
+ WHERE " . $db->sql_in_set($column, $from_values);
+ $result = $db->sql_query($sql);
+
+ $old_user_ids = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $old_user_ids[$row[$column]][] = (int) $row['user_id'];
+ }
+ $db->sql_freeresult($result);
+
+ $sql = "SELECT $column, user_id
+ FROM $table
+ WHERE $column = " . (int) $to_value;
+ $result = $db->sql_query($sql);
+
+ $new_user_ids = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $new_user_ids[$row[$column]][] = (int) $row['user_id'];
+ }
+ $db->sql_freeresult($result);
+
+ $queries = array();
+ foreach ($from_values as $from_value)
+ {
+ if (!isset($old_user_ids[$from_value]))
+ {
+ continue;
+ }
+ if (empty($new_user_ids))
+ {
+ $sql = "UPDATE $table
+ SET $column = " . (int) $to_value . "
+ WHERE $column = '" . $db->sql_escape($from_value) . "'";
+ $queries[] = $sql;
+ }
+ else
+ {
+ $different_user_ids = array_diff($old_user_ids[$from_value], $new_user_ids[$to_value]);
+ if (!empty($different_user_ids))
+ {
+ $sql = "UPDATE $table
+ SET $column = " . (int) $to_value . "
+ WHERE $column = '" . $db->sql_escape($from_value) . "'
+ AND " . $db->sql_in_set('user_id', $different_user_ids);
+ $queries[] = $sql;
+ }
+ }
+ }
+
+ if (!empty($queries))
+ {
+ $db->sql_transaction('begin');
+
+ foreach ($queries as $sql)
+ {
+ $db->sql_query($sql);
+ }
+
+ $sql = "DELETE FROM $table
+ WHERE " . $db->sql_in_set($column, $from_values);
+ $db->sql_query($sql);
+
+ $db->sql_transaction('commit');
+ }
+}
+
+/**
+* Updates rows in given table from a set of values to a new value.
+* If this results in rows violating uniqueness constraints, the duplicate
+* rows are merged respecting notify_status (0 takes precedence over 1).
+*
+* The only supported table is topics_watch.
+*
+* @param phpbb_db_driver $db Database object
+* @param string $table Table on which to perform the update
+* @param string $column Column whose values to change
+* @param array $from_values An array of values that should be changed
+* @param int $to_value The new value
+* @return null
+*/
+function phpbb_update_rows_avoiding_duplicates_notify_status(phpbb_db_driver $db, $table, $column, $from_values, $to_value)
+{
+ $sql = "SELECT $column, user_id, notify_status
+ FROM $table
+ WHERE " . $db->sql_in_set($column, $from_values);
+ $result = $db->sql_query($sql);
+
+ $old_user_ids = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $old_user_ids[(int) $row['notify_status']][$row[$column]][] = (int) $row['user_id'];
+ }
+ $db->sql_freeresult($result);
+
+ $sql = "SELECT $column, user_id
+ FROM $table
+ WHERE $column = " . (int) $to_value;
+ $result = $db->sql_query($sql);
+
+ $new_user_ids = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $new_user_ids[$row[$column]][] = (int) $row['user_id'];
+ }
+ $db->sql_freeresult($result);
+
+ $queries = array();
+ $extra_updates = array(
+ 0 => 'notify_status = 0',
+ 1 => '',
+ );
+ foreach ($from_values as $from_value)
+ {
+ foreach ($extra_updates as $notify_status => $extra_update)
+ {
+ if (!isset($old_user_ids[$notify_status][$from_value]))
+ {
+ continue;
+ }
+ if (empty($new_user_ids))
+ {
+ $sql = "UPDATE $table
+ SET $column = " . (int) $to_value . "
+ WHERE $column = '" . $db->sql_escape($from_value) . "'";
+ $queries[] = $sql;
+ }
+ else
+ {
+ $different_user_ids = array_diff($old_user_ids[$notify_status][$from_value], $new_user_ids[$to_value]);
+ if (!empty($different_user_ids))
+ {
+ $sql = "UPDATE $table
+ SET $column = " . (int) $to_value . "
+ WHERE $column = '" . $db->sql_escape($from_value) . "'
+ AND " . $db->sql_in_set('user_id', $different_user_ids);
+ $queries[] = $sql;
+ }
+
+ if ($extra_update)
+ {
+ $same_user_ids = array_diff($old_user_ids[$notify_status][$from_value], $different_user_ids);
+ if (!empty($same_user_ids))
+ {
+ $sql = "UPDATE $table
+ SET $extra_update
+ WHERE $column = '" . (int) $to_value . "'
+ AND " . $db->sql_in_set('user_id', $same_user_ids);
+ $queries[] = $sql;
+ }
+ }
+ }
+ }
+ }
+
+ if (!empty($queries))
+ {
+ $db->sql_transaction('begin');
+
+ foreach ($queries as $sql)
+ {
+ $db->sql_query($sql);
+ }
+
+ $sql = "DELETE FROM $table
+ WHERE " . $db->sql_in_set($column, $from_values);
+ $db->sql_query($sql);
+
+ $db->sql_transaction('commit');
+ }
+}
diff --git a/phpBB/includes/functions_display.php b/phpBB/includes/functions_display.php
index 73129803ee..cd4c901b58 100644
--- a/phpBB/includes/functions_display.php
+++ b/phpBB/includes/functions_display.php
@@ -61,6 +61,20 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod
{
markread('all', false, false, request_var('mark_time', 0));
+ if ($request->is_ajax())
+ {
+ // Tell the ajax script what language vars and URL need to be replaced
+ $data = array(
+ 'NO_UNREAD_POSTS' => $user->lang['NO_UNREAD_POSTS'],
+ 'UNREAD_POSTS' => $user->lang['UNREAD_POSTS'],
+ 'U_MARK_FORUMS' => ($user->data['is_registered'] || $config['load_anon_lastread']) ? append_sid("{$phpbb_root_path}index.$phpEx", 'hash=' . generate_link_hash('global') . '&mark=forums&mark_time=' . time()) : '',
+ 'MESSAGE_TITLE' => $user->lang['INFORMATION'],
+ 'MESSAGE_TEXT' => $user->lang['FORUMS_MARKED']
+ );
+ $json_response = new phpbb_json_response();
+ $json_response->send($data);
+ }
+
trigger_error(
$user->lang['FORUMS_MARKED'] . '<br /><br />' .
sprintf($user->lang['RETURN_INDEX'], '<a href="' . $redirect . '">', '</a>')
@@ -313,6 +327,20 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod
$message = sprintf($user->lang['RETURN_FORUM'], '<a href="' . $redirect . '">', '</a>');
meta_refresh(3, $redirect);
+ if ($request->is_ajax())
+ {
+ // Tell the ajax script what language vars and URL need to be replaced
+ $data = array(
+ 'NO_UNREAD_POSTS' => $user->lang['NO_UNREAD_POSTS'],
+ 'UNREAD_POSTS' => $user->lang['UNREAD_POSTS'],
+ 'U_MARK_FORUMS' => ($user->data['is_registered'] || $config['load_anon_lastread']) ? append_sid("{$phpbb_root_path}viewforum.$phpEx", 'hash=' . generate_link_hash('global') . '&f=' . $root_data['forum_id'] . '&mark=forums&mark_time=' . time()) : '',
+ 'MESSAGE_TITLE' => $user->lang['INFORMATION'],
+ 'MESSAGE_TEXT' => $user->lang['FORUMS_MARKED']
+ );
+ $json_response = new phpbb_json_response();
+ $json_response->send($data);
+ }
+
trigger_error($user->lang['FORUMS_MARKED'] . '<br /><br />' . $message);
}
else
diff --git a/phpBB/includes/functions_download.php b/phpBB/includes/functions_download.php
index b6371dbecc..9ae647d806 100644
--- a/phpBB/includes/functions_download.php
+++ b/phpBB/includes/functions_download.php
@@ -433,7 +433,7 @@ function set_modified_headers($stamp, $browser)
*
* @param bool $exit Whether to die or not.
*
-* @return void
+* @return null
*/
function file_gc($exit = true)
{
@@ -596,7 +596,7 @@ function phpbb_parse_range_request($request_array, $filesize)
/**
* Increments the download count of all provided attachments
*
-* @param dbal $db The database object
+* @param phpbb_db_driver $db The database object
* @param array|int $ids The attach_id of each attachment
*
* @return null
@@ -617,7 +617,7 @@ function phpbb_increment_downloads($db, $ids)
/**
* Handles authentication when downloading attachments from a post or topic
*
-* @param dbal $db The database object
+* @param phpbb_db_driver $db The database object
* @param phpbb_auth $auth The authentication object
* @param int $topic_id The id of the topic that we are downloading from
*
@@ -651,7 +651,7 @@ function phpbb_download_handle_forum_auth($db, $auth, $topic_id)
/**
* Handles authentication when downloading attachments from PMs
*
-* @param dbal $db The database object
+* @param phpbb_db_driver $db The database object
* @param phpbb_auth $auth The authentication object
* @param int $user_id The user id
* @param int $msg_id The id of the PM that we are downloading from
@@ -678,7 +678,7 @@ function phpbb_download_handle_pm_auth($db, $auth, $user_id, $msg_id)
/**
* Checks whether a user can download from a particular PM
*
-* @param dbal $db The database object
+* @param phpbb_db_driver $db The database object
* @param int $user_id The user id
* @param int $msg_id The id of the PM that we are downloading from
*
diff --git a/phpBB/includes/functions_install.php b/phpBB/includes/functions_install.php
index 7a799993db..8978e3fadd 100644
--- a/phpBB/includes/functions_install.php
+++ b/phpBB/includes/functions_install.php
@@ -28,16 +28,18 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20
'SCHEMA' => 'firebird',
'MODULE' => 'interbase',
'DELIM' => ';;',
- 'DRIVER' => 'firebird',
+ 'DRIVER' => 'phpbb_db_driver_firebird',
'AVAILABLE' => true,
'2.0.x' => false,
),
+ // Note: php 5.5 alpha 2 deprecated mysql.
+ // Keep mysqli before mysql in this list.
'mysqli' => array(
'LABEL' => 'MySQL with MySQLi Extension',
'SCHEMA' => 'mysql_41',
'MODULE' => 'mysqli',
'DELIM' => ';',
- 'DRIVER' => 'mysqli',
+ 'DRIVER' => 'phpbb_db_driver_mysqli',
'AVAILABLE' => true,
'2.0.x' => true,
),
@@ -46,7 +48,7 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20
'SCHEMA' => 'mysql',
'MODULE' => 'mysql',
'DELIM' => ';',
- 'DRIVER' => 'mysql',
+ 'DRIVER' => 'phpbb_db_driver_mysql',
'AVAILABLE' => true,
'2.0.x' => true,
),
@@ -55,7 +57,7 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20
'SCHEMA' => 'mssql',
'MODULE' => 'mssql',
'DELIM' => 'GO',
- 'DRIVER' => 'mssql',
+ 'DRIVER' => 'phpbb_db_driver_mssql',
'AVAILABLE' => true,
'2.0.x' => true,
),
@@ -64,7 +66,7 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20
'SCHEMA' => 'mssql',
'MODULE' => 'odbc',
'DELIM' => 'GO',
- 'DRIVER' => 'mssql_odbc',
+ 'DRIVER' => 'phpbb_db_driver_mssql_odbc',
'AVAILABLE' => true,
'2.0.x' => true,
),
@@ -73,7 +75,7 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20
'SCHEMA' => 'mssql',
'MODULE' => 'sqlsrv',
'DELIM' => 'GO',
- 'DRIVER' => 'mssqlnative',
+ 'DRIVER' => 'phpbb_db_driver_mssqlnative',
'AVAILABLE' => true,
'2.0.x' => false,
),
@@ -82,16 +84,16 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20
'SCHEMA' => 'oracle',
'MODULE' => 'oci8',
'DELIM' => '/',
- 'DRIVER' => 'oracle',
+ 'DRIVER' => 'phpbb_db_driver_oracle',
'AVAILABLE' => true,
'2.0.x' => false,
),
'postgres' => array(
- 'LABEL' => 'PostgreSQL 7.x/8.x',
+ 'LABEL' => 'PostgreSQL 8.3+',
'SCHEMA' => 'postgres',
'MODULE' => 'pgsql',
'DELIM' => ';',
- 'DRIVER' => 'postgres',
+ 'DRIVER' => 'phpbb_db_driver_postgres',
'AVAILABLE' => true,
'2.0.x' => true,
),
@@ -100,7 +102,7 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20
'SCHEMA' => 'sqlite',
'MODULE' => 'sqlite',
'DELIM' => ';',
- 'DRIVER' => 'sqlite',
+ 'DRIVER' => 'phpbb_db_driver_sqlite',
'AVAILABLE' => true,
'2.0.x' => false,
),
@@ -205,26 +207,19 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix,
$dbms = $dbms_details['DRIVER'];
- if ($load_dbal)
- {
- // Include the DB layer
- include($phpbb_root_path . 'includes/db/' . $dbms . '.' . $phpEx);
- }
-
// Instantiate it and set return on error true
- $sql_db = 'dbal_' . $dbms;
- $db = new $sql_db();
+ $db = new $dbms();
$db->sql_return_on_error(true);
// Check that we actually have a database name before going any further.....
- if ($dbms_details['DRIVER'] != 'sqlite' && $dbms_details['DRIVER'] != 'oracle' && $dbname === '')
+ if ($dbms_details['DRIVER'] != 'phpbb_db_driver_sqlite' && $dbms_details['DRIVER'] != 'phpbb_db_driver_oracle' && $dbname === '')
{
$error[] = $lang['INST_ERR_DB_NO_NAME'];
return false;
}
// Make sure we don't have a daft user who thinks having the SQLite database in the forum directory is a good idea
- if ($dbms_details['DRIVER'] == 'sqlite' && stripos(phpbb_realpath($dbhost), phpbb_realpath('../')) === 0)
+ if ($dbms_details['DRIVER'] == 'phpbb_db_driver_sqlite' && stripos(phpbb_realpath($dbhost), phpbb_realpath('../')) === 0)
{
$error[] = $lang['INST_ERR_DB_FORUM_PATH'];
return false;
@@ -233,8 +228,8 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix,
// Check the prefix length to ensure that index names are not too long and does not contain invalid characters
switch ($dbms_details['DRIVER'])
{
- case 'mysql':
- case 'mysqli':
+ case 'phpbb_db_driver_mysql':
+ case 'phpbb_db_driver_mysqli':
if (strspn($table_prefix, '-./\\') !== 0)
{
$error[] = $lang['INST_ERR_PREFIX_INVALID'];
@@ -243,22 +238,22 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix,
// no break;
- case 'postgres':
+ case 'phpbb_db_driver_postgres':
$prefix_length = 36;
break;
- case 'mssql':
- case 'mssql_odbc':
- case 'mssqlnative':
+ case 'phpbb_db_driver_mssql':
+ case 'phpbb_db_driver_mssql_odbc':
+ case 'phpbb_db_driver_mssqlnative':
$prefix_length = 90;
break;
- case 'sqlite':
+ case 'phpbb_db_driver_sqlite':
$prefix_length = 200;
break;
- case 'firebird':
- case 'oracle':
+ case 'phpbb_db_driver_firebird':
+ case 'phpbb_db_driver_oracle':
$prefix_length = 6;
break;
}
@@ -296,21 +291,21 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix,
// Make sure that the user has selected a sensible DBAL for the DBMS actually installed
switch ($dbms_details['DRIVER'])
{
- case 'mysqli':
+ case 'phpbb_db_driver_mysqli':
if (version_compare(mysqli_get_server_info($db->db_connect_id), '4.1.3', '<'))
{
$error[] = $lang['INST_ERR_DB_NO_MYSQLI'];
}
break;
- case 'sqlite':
+ case 'phpbb_db_driver_sqlite':
if (version_compare(sqlite_libversion(), '2.8.2', '<'))
{
$error[] = $lang['INST_ERR_DB_NO_SQLITE'];
}
break;
- case 'firebird':
+ case 'phpbb_db_driver_firebird':
// check the version of FB, use some hackery if we can't get access to the server info
if ($db->service_handle !== false && function_exists('ibase_server_info'))
{
@@ -391,7 +386,7 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix,
}
break;
- case 'oracle':
+ case 'phpbb_db_driver_oracle':
if ($unicode_check)
{
$sql = "SELECT *
@@ -413,7 +408,7 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix,
}
break;
- case 'postgres':
+ case 'phpbb_db_driver_postgres':
if ($unicode_check)
{
$sql = "SHOW server_encoding;";
@@ -515,6 +510,9 @@ function phpbb_create_config_file_data($data, $dbms, $debug = false, $debug_test
'dbuser' => $data['dbuser'],
'dbpasswd' => htmlspecialchars_decode($data['dbpasswd']),
'table_prefix' => $data['table_prefix'],
+
+ 'adm_relative_path' => 'adm/',
+
'acm_type' => 'phpbb_cache_driver_file',
);
diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php
index cf03de08c4..d0a02567ad 100644
--- a/phpBB/includes/functions_messenger.php
+++ b/phpBB/includes/functions_messenger.php
@@ -210,7 +210,7 @@ class messenger
{
$style_resource_locator = new phpbb_style_resource_locator();
$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider());
- $tpl = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context());
+ $tpl = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager);
$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $tpl);
$this->tpl_msg[$template_lang . $template_file] = $tpl;
@@ -231,7 +231,7 @@ class messenger
}
}
- $style->set_custom_style($template_lang . '_email', array($template_path, $fallback_template_path), '');
+ $style->set_custom_style($template_lang . '_email', array($template_path, $fallback_template_path), array(), '');
$tpl->set_filenames(array(
'body' => $template_file . '.txt',
@@ -651,64 +651,6 @@ class queue
}
/**
- * Obtains exclusive lock on queue cache file.
- * Returns resource representing the lock
- */
- function lock()
- {
- // For systems that can't have two processes opening
- // one file for writing simultaneously
- if (file_exists($this->cache_file . '.lock'))
- {
- $mode = 'rb';
- }
- else
- {
- $mode = 'wb';
- }
-
- $lock_fp = @fopen($this->cache_file . '.lock', $mode);
-
- if ($mode == 'wb')
- {
- if (!$lock_fp)
- {
- // Two processes may attempt to create lock file at the same time.
- // Have the losing process try opening the lock file again for reading
- // on the assumption that the winning process created it
- $mode = 'rb';
- $lock_fp = @fopen($this->cache_file . '.lock', $mode);
- }
- else
- {
- // Only need to set mode when the lock file is written
- @chmod($this->cache_file . '.lock', 0666);
- }
- }
-
- if ($lock_fp)
- {
- @flock($lock_fp, LOCK_EX);
- }
-
- return $lock_fp;
- }
-
- /**
- * Releases lock on queue cache file, using resource obtained from lock()
- */
- function unlock($lock_fp)
- {
- // lock() will return null if opening lock file, and thus locking, failed.
- // Accept null values here so that client code does not need to check them
- if ($lock_fp)
- {
- @flock($lock_fp, LOCK_UN);
- fclose($lock_fp);
- }
- }
-
- /**
* Process queue
* Using lock file
*/
@@ -716,16 +658,24 @@ class queue
{
global $db, $config, $phpEx, $phpbb_root_path, $user;
- $lock_fp = $this->lock();
+ $lock = new phpbb_lock_flock($this->cache_file);
+ $lock->acquire();
- set_config('last_queue_run', time(), true);
-
- if (!file_exists($this->cache_file) || filemtime($this->cache_file) > time() - $config['queue_interval'])
+ // avoid races, check file existence once
+ $have_cache_file = file_exists($this->cache_file);
+ if (!$have_cache_file || $config['last_queue_run'] > time() - $config['queue_interval'])
{
- $this->unlock($lock_fp);
+ if (!$have_cache_file)
+ {
+ set_config('last_queue_run', time(), true);
+ }
+
+ $lock->release();
return;
}
+ set_config('last_queue_run', time(), true);
+
include($this->cache_file);
foreach ($this->queue_data as $object => $data_ary)
@@ -789,7 +739,7 @@ class queue
break;
default:
- $this->unlock($lock_fp);
+ $lock->release();
return;
}
@@ -865,7 +815,7 @@ class queue
}
}
- $this->unlock($lock_fp);
+ $lock->release();
}
/**
@@ -878,7 +828,8 @@ class queue
return;
}
- $lock_fp = $this->lock();
+ $lock = new phpbb_lock_flock($this->cache_file);
+ $lock->acquire();
if (file_exists($this->cache_file))
{
@@ -905,7 +856,7 @@ class queue
phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
}
- $this->unlock($lock_fp);
+ $lock->release();
}
}
diff --git a/phpBB/includes/functions_module.php b/phpBB/includes/functions_module.php
index ad76be9f2f..0d387ace6d 100644
--- a/phpBB/includes/functions_module.php
+++ b/phpBB/includes/functions_module.php
@@ -759,7 +759,26 @@ class p_master
}
}
- $u_title = $module_url . $delim . 'i=' . (($item_ary['cat']) ? $item_ary['id'] : $item_ary['name'] . (($item_ary['is_duplicate']) ? '&amp;icat=' . $current_id : '') . '&amp;mode=' . $item_ary['mode']);
+ $u_title = $module_url . $delim . 'i=';
+ // if the item has a name use it, else use its id
+ if (empty($item_ary['name']))
+ {
+ $u_title .= $item_ary['id'];
+ }
+ else
+ {
+ // if the category has a name, then use it.
+ $u_title .= $item_ary['name'];
+ }
+ // If the item is not a category append the mode
+ if (!$item_ary['cat'])
+ {
+ if ($item_ary['is_duplicate'])
+ {
+ $u_title .= '&amp;icat=' . $current_id;
+ }
+ $u_title .= '&amp;mode=' . $item_ary['mode'];
+ }
// Was not allowed in categories before - /*!$item_ary['cat'] && */
$u_title .= (isset($item_ary['url_extra'])) ? $item_ary['url_extra'] : '';
diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php
index 171cf988ce..8aea27a9ef 100644
--- a/phpBB/includes/functions_posting.php
+++ b/phpBB/includes/functions_posting.php
@@ -1695,8 +1695,9 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u
// The variable name should be $post_approved, because it indicates if the post is approved or not
$post_approval = 1;
- // Check the permissions for post approval. Moderators are not affected.
- if (!$auth->acl_get('f_noapprove', $data['forum_id']) && !$auth->acl_get('m_approve', $data['forum_id']))
+ // Check the permissions for post approval.
+ // Moderators must go through post approval like ordinary users.
+ if (!$auth->acl_get('f_noapprove', $data['forum_id']))
{
// Post not approved, but in queue
$post_approval = 0;
diff --git a/phpBB/includes/functions_upload.php b/phpBB/includes/functions_upload.php
index b467aa93d1..4f31a85e83 100644
--- a/phpBB/includes/functions_upload.php
+++ b/phpBB/includes/functions_upload.php
@@ -70,7 +70,7 @@ class filespec
$this->mimetype = 'application/octetstream';
}
- $this->extension = strtolower($this->get_extension($this->realname));
+ $this->extension = strtolower(self::get_extension($this->realname));
// Try to get real filesize from temporary folder (not always working) ;)
$this->filesize = (@filesize($this->filename)) ? @filesize($this->filename) : $this->filesize;
@@ -187,8 +187,11 @@ class filespec
/**
* Get file extension
+ *
+ * @param string Filename that needs to be checked
+ * @return string Extension of the supplied filename
*/
- function get_extension($filename)
+ static public function get_extension($filename)
{
if (strpos($filename, '.') === false)
{
@@ -369,7 +372,7 @@ class filespec
}
// Check image type
- $types = $this->upload->image_types();
+ $types = fileupload::image_types();
if (!isset($types[$this->image_info[2]]) || !in_array($this->extension, $types[$this->image_info[2]]))
{
@@ -1019,9 +1022,11 @@ class fileupload
}
/**
- * Return image type/extension mapping
+ * Get image type/extension mapping
+ *
+ * @return array Array containing the image types and their extensions
*/
- function image_types()
+ static public function image_types()
{
return array(
IMAGETYPE_GIF => array('gif'),
diff --git a/phpBB/includes/functions_url_matcher.php b/phpBB/includes/functions_url_matcher.php
new file mode 100644
index 0000000000..7280cb74eb
--- /dev/null
+++ b/phpBB/includes/functions_url_matcher.php
@@ -0,0 +1,106 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Create a new UrlMatcher class and dump it into the cache file
+*
+* @param phpbb_extension_finder $finder Extension finder
+* @param RequestContext $context Symfony RequestContext object
+* @param string $root_path Root path
+* @param string $php_ext PHP extension
+* @return null
+*/
+function phpbb_get_url_matcher(phpbb_extension_finder $finder, RequestContext $context, $root_path, $php_ext)
+{
+ if (defined('DEBUG'))
+ {
+ return phpbb_create_url_matcher($finder, $context);
+ }
+
+ if (!phpbb_url_matcher_dumped($root_path, $php_ext))
+ {
+ phpbb_create_dumped_url_matcher($finder, $root_path, $php_ext);
+ }
+
+ return phpbb_load_url_matcher($context, $root_path, $php_ext);
+}
+
+/**
+* Create a new UrlMatcher class and dump it into the cache file
+*
+* @param phpbb_extension_finder $finder Extension finder
+* @param string $root_path Root path
+* @param string $php_ext PHP extension
+* @return null
+*/
+function phpbb_create_dumped_url_matcher(phpbb_extension_finder $finder, $root_path, $php_ext)
+{
+ $provider = new phpbb_controller_provider();
+ $routes = $provider->import_paths_from_finder($finder)->find();
+ $dumper = new PhpMatcherDumper($routes);
+ $cached_url_matcher_dump = $dumper->dump(array(
+ 'class' => 'phpbb_url_matcher',
+ ));
+
+ file_put_contents($root_path . 'cache/url_matcher' . $php_ext, $cached_url_matcher_dump);
+}
+
+/**
+* Create a non-cached UrlMatcher
+*
+* @param phpbb_extension_finder $finder Extension finder
+* @param RequestContext $context Symfony RequestContext object
+* @return UrlMatcher
+*/
+function phpbb_create_url_matcher(phpbb_extension_finder $finder, RequestContext $context)
+{
+ $provider = new phpbb_controller_provider();
+ $routes = $provider->import_paths_from_finder($finder)->find();
+ return new UrlMatcher($routes, $context);
+}
+
+/**
+* Load the cached phpbb_url_matcher class
+*
+* @param RequestContext $context Symfony RequestContext object
+* @param string $root_path Root path
+* @param string $php_ext PHP extension
+* @return phpbb_url_matcher
+*/
+function phpbb_load_url_matcher(RequestContext $context, $root_path, $php_ext)
+{
+ require($root_path . 'cache/url_matcher' . $php_ext);
+ return new phpbb_url_matcher($context);
+}
+
+/**
+* Determine whether we have our dumped URL matcher
+*
+* The class is automatically dumped to the cache directory
+*
+* @param string $root_path Root path
+* @param string $php_ext PHP extension
+* @return bool True if it exists, false if not
+*/
+function phpbb_url_matcher_dumped($root_path, $php_ext)
+{
+ return file_exists($root_path . 'cache/url_matcher' . $php_ext);
+}
diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php
index 8f9c9198f4..b7878ddfc7 100644
--- a/phpBB/includes/functions_user.php
+++ b/phpBB/includes/functions_user.php
@@ -2698,12 +2698,12 @@ function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow
}
$db->sql_freeresult($result);
- if (isset($sql_ary['group_avatar']) && !$sql_ary['group_avatar'])
+ if (isset($sql_ary['group_avatar']))
{
remove_default_avatar($group_id, $user_ary);
}
- if (isset($sql_ary['group_rank']) && !$sql_ary['group_rank'])
+ if (isset($sql_ary['group_rank']))
{
remove_default_rank($group_id, $user_ary);
}
@@ -2842,7 +2842,7 @@ function avatar_remove_db($avatar_name)
*/
function group_delete($group_id, $group_name = false)
{
- global $db, $phpbb_root_path, $phpEx, $phpbb_dispatcher;
+ global $db, $cache, $auth, $phpbb_root_path, $phpEx, $phpbb_dispatcher;
if (!$group_name)
{
@@ -2913,12 +2913,12 @@ function group_delete($group_id, $group_name = false)
extract($phpbb_dispatcher->trigger_event('core.delete_group_after', compact($vars)));
// Re-cache moderators
- if (!function_exists('cache_moderators'))
+ if (!function_exists('phpbb_cache_moderators'))
{
include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
}
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
add_log('admin', 'LOG_GROUP_DELETE', $group_name);
@@ -3208,8 +3208,8 @@ function remove_default_avatar($group_id, $user_ids)
user_avatar_width = 0,
user_avatar_height = 0
WHERE group_id = " . (int) $group_id . "
- AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
- AND " . $db->sql_in_set('user_id', $user_ids);
+ AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
+ AND " . $db->sql_in_set('user_id', $user_ids);
$db->sql_query($sql);
}
@@ -3246,9 +3246,9 @@ function remove_default_rank($group_id, $user_ids)
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_rank = 0
WHERE group_id = ' . (int)$group_id . '
- AND user_rank <> 0
- AND user_rank = ' . (int)$row['group_rank'] . '
- AND ' . $db->sql_in_set('user_id', $user_ids);
+ AND user_rank <> 0
+ AND user_rank = ' . (int)$row['group_rank'] . '
+ AND ' . $db->sql_in_set('user_id', $user_ids);
$db->sql_query($sql);
}
@@ -3277,7 +3277,8 @@ function group_user_attributes($action, $group_id, $user_id_ary = false, $userna
case 'demote':
case 'promote':
- $sql = 'SELECT user_id FROM ' . USER_GROUP_TABLE . "
+ $sql = 'SELECT user_id
+ FROM ' . USER_GROUP_TABLE . "
WHERE group_id = $group_id
AND user_pending = 1
AND " . $db->sql_in_set('user_id', $user_id_ary);
@@ -3375,7 +3376,8 @@ function group_user_attributes($action, $group_id, $user_id_ary = false, $userna
return 'NO_USERS';
}
- $sql = 'SELECT user_id, group_id FROM ' . USERS_TABLE . '
+ $sql = 'SELECT user_id, group_id
+ FROM ' . USERS_TABLE . '
WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
$result = $db->sql_query($sql);
@@ -3463,7 +3465,7 @@ function group_validate_groupname($group_id, $group_name)
*/
function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false)
{
- global $cache, $db, $phpbb_dispatcher;
+ global $phpbb_container, $db, $phpbb_dispatcher;
if (empty($user_id_ary))
{
@@ -3509,45 +3511,69 @@ function group_set_user_default($group_id, $user_id_ary, $group_attributes = fal
}
}
- // Before we update the user attributes, we will make a list of those having now the group avatar assigned
- if (isset($sql_ary['user_avatar']))
+ $updated_sql_ary = $sql_ary;
+
+ // Before we update the user attributes, we will update the rank for users that don't have a custom rank
+ if (isset($sql_ary['user_rank']))
{
- // Ok, get the original avatar data from users having an uploaded one (we need to remove these from the filesystem)
- $sql = 'SELECT user_id, group_id, user_avatar
- FROM ' . USERS_TABLE . '
- WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . '
- AND user_avatar_type = ' . AVATAR_UPLOAD;
- $result = $db->sql_query($sql);
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', array('user_rank' => $sql_ary['user_rank'])) . '
+ WHERE user_rank = 0
+ AND ' . $db->sql_in_set('user_id', $user_id_ary);
+ $db->sql_query($sql);
+ unset($sql_ary['user_rank']);
+ }
- while ($row = $db->sql_fetchrow($result))
+ // Before we update the user attributes, we will update the avatar for users that don't have a custom avatar
+ $avatar_options = array('user_avatar', 'user_avatar_type', 'user_avatar_height', 'user_avatar_width');
+
+ if (isset($sql_ary['user_avatar']))
+ {
+ $avatar_sql_ary = array();
+ foreach ($avatar_options as $avatar_option)
{
- avatar_delete('user', $row);
- }
- $db->sql_freeresult($result);
+ if (isset($sql_ary[$avatar_option]))
+ {
+ $avatar_sql_ary[$avatar_option] = $sql_ary[$avatar_option];
+ }
+ }
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $avatar_sql_ary) . "
+ WHERE user_avatar = ''
+ AND " . $db->sql_in_set('user_id', $user_id_ary);
+ $db->sql_query($sql);
}
- else
+
+ // Remove the avatar options, as we already updated them
+ foreach ($avatar_options as $avatar_option)
{
- unset($sql_ary['user_avatar_type']);
- unset($sql_ary['user_avatar_height']);
- unset($sql_ary['user_avatar_width']);
+ unset($sql_ary[$avatar_option]);
}
- $sql = 'UPDATE ' . USERS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
- WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
- $db->sql_query($sql);
+ if (!empty($sql_ary))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
+ $db->sql_query($sql);
+ }
if (isset($sql_ary['user_colour']))
{
// Update any cached colour information for these users
- $sql = 'UPDATE ' . FORUMS_TABLE . " SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
+ $sql = 'UPDATE ' . FORUMS_TABLE . "
+ SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
$db->sql_query($sql);
- $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
+ $sql = 'UPDATE ' . TOPICS_TABLE . "
+ SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
$db->sql_query($sql);
- $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
+ $sql = 'UPDATE ' . TOPICS_TABLE . "
+ SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
$db->sql_query($sql);
@@ -3559,6 +3585,9 @@ function group_set_user_default($group_id, $user_id_ary, $group_attributes = fal
}
}
+ // Make all values available for the event
+ $sql_ary = $updated_sql_ary;
+
/**
* Event when the default group is set for an array of users
*
@@ -3579,7 +3608,7 @@ function group_set_user_default($group_id, $user_id_ary, $group_attributes = fal
}
// Because some tables/caches use usercolour-specific data we need to purge this here.
- $cache->destroy('sql', MODERATOR_CACHE_TABLE);
+ $phpbb_container->get('cache.driver')->destroy('sql', MODERATOR_CACHE_TABLE);
}
/**
@@ -3678,7 +3707,7 @@ function group_memberships($group_id_ary = false, $user_id_ary = false, $return_
*/
function group_update_listings($group_id)
{
- global $auth;
+ global $db, $cache, $auth;
$hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_'));
@@ -3720,22 +3749,22 @@ function group_update_listings($group_id)
if ($mod_permissions)
{
- if (!function_exists('cache_moderators'))
+ if (!function_exists('phpbb_cache_moderators'))
{
global $phpbb_root_path, $phpEx;
include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
}
- cache_moderators();
+ phpbb_cache_moderators($db, $cache, $auth);
}
if ($mod_permissions || $admin_permissions)
{
- if (!function_exists('update_foes'))
+ if (!function_exists('phpbb_update_foes'))
{
global $phpbb_root_path, $phpEx;
include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
}
- update_foes(array($group_id));
+ phpbb_update_foes($db, $auth, array($group_id));
}
}
diff --git a/phpBB/includes/group_positions.php b/phpBB/includes/group_positions.php
index 74de3516cb..60352ed97d 100644
--- a/phpBB/includes/group_positions.php
+++ b/phpBB/includes/group_positions.php
@@ -104,7 +104,7 @@ class phpbb_group_positions
* Addes a group by group_id
*
* @param int $group_id group_id of the group to be added
- * @return void
+ * @return null
*/
public function add_group($group_id)
{
@@ -128,7 +128,7 @@ class phpbb_group_positions
*
* @param int $group_id group_id of the group to be deleted
* @param bool $skip_group Skip setting the group to GROUP_DISABLED, to save the query, when you need to update it anyway.
- * @return void
+ * @return null
*/
public function delete_group($group_id, $skip_group = false)
{
@@ -159,7 +159,7 @@ class phpbb_group_positions
* Moves a group up by group_id
*
* @param int $group_id group_id of the group to be moved
- * @return void
+ * @return null
*/
public function move_up($group_id)
{
@@ -170,7 +170,7 @@ class phpbb_group_positions
* Moves a group down by group_id
*
* @param int $group_id group_id of the group to be moved
- * @return void
+ * @return null
*/
public function move_down($group_id)
{
@@ -184,7 +184,7 @@ class phpbb_group_positions
* @param int $delta number of steps:
* - positive = move up
* - negative = move down
- * @return void
+ * @return null
*/
public function move($group_id, $delta)
{
diff --git a/phpBB/includes/hook/finder.php b/phpBB/includes/hook/finder.php
new file mode 100644
index 0000000000..065e685514
--- /dev/null
+++ b/phpBB/includes/hook/finder.php
@@ -0,0 +1,84 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* The hook finder locates installed hooks.
+*
+* @package phpBB3
+*/
+class phpbb_hook_finder
+{
+ protected $phpbb_root_path;
+ protected $cache;
+ protected $php_ext;
+
+ /**
+ * Creates a new finder instance.
+ *
+ * @param string $phpbb_root_path Path to the phpbb root directory
+ * @param string $php_ext php file extension
+ * @param phpbb_cache_driver_interface $cache A cache instance or null
+ */
+ public function __construct($phpbb_root_path, $php_ext, phpbb_cache_driver_interface $cache = null)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->cache = $cache;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Finds all hook files.
+ *
+ * @param bool $cache Whether the result should be cached
+ * @return array An array of paths to found hook files
+ */
+ public function find($cache = true)
+ {
+ if (!defined('DEBUG') && $cache && $this->cache)
+ {
+ $hook_files = $this->cache->get('_hooks');
+ if ($hook_files !== false)
+ {
+ return $hook_files;
+ }
+ }
+
+ $hook_files = array();
+
+ // Now search for hooks...
+ $dh = @opendir($this->phpbb_root_path . 'includes/hooks/');
+
+ if ($dh)
+ {
+ while (($file = readdir($dh)) !== false)
+ {
+ if (strpos($file, 'hook_') === 0 && substr($file, -(strlen($this->php_ext) + 1)) === '.' . $this->php_ext)
+ {
+ $hook_files[] = substr($file, 0, -(strlen($this->php_ext) + 1));
+ }
+ }
+ closedir($dh);
+ }
+
+ if ($cache && $this->cache)
+ {
+ $this->cache->put('_hooks', $hook_files);
+ }
+
+ return $hook_files;
+ }
+}
diff --git a/phpBB/includes/lock/db.php b/phpBB/includes/lock/db.php
index fa559d6887..ccdaed0b28 100644
--- a/phpBB/includes/lock/db.php
+++ b/phpBB/includes/lock/db.php
@@ -48,7 +48,7 @@ class phpbb_lock_db
/**
* A database connection
- * @var dbal
+ * @var phpbb_db_driver
*/
private $db;
@@ -59,9 +59,9 @@ class phpbb_lock_db
*
* @param string $config_name A config variable to be used for locking
* @param array $config The phpBB configuration
- * @param dbal $db A database connection
+ * @param phpbb_db_driver $db A database connection
*/
- public function __construct($config_name, phpbb_config $config, dbal $db)
+ public function __construct($config_name, phpbb_config $config, phpbb_db_driver $db)
{
$this->config_name = $config_name;
$this->config = $config;
@@ -125,7 +125,7 @@ class phpbb_lock_db
* Note: Attempting to release a lock that is already released,
* that is, calling release() multiple times, is harmless.
*
- * @return void
+ * @return null
*/
public function release()
{
diff --git a/phpBB/includes/lock/flock.php b/phpBB/includes/lock/flock.php
new file mode 100644
index 0000000000..97bc7dd2b9
--- /dev/null
+++ b/phpBB/includes/lock/flock.php
@@ -0,0 +1,133 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* File locking class
+* @package phpBB3
+*/
+class phpbb_lock_flock
+{
+ /**
+ * Path to the file to which access is controlled
+ *
+ * @var string
+ */
+ private $path;
+
+ /**
+ * File pointer for the lock file
+ * @var string
+ */
+ private $lock_fp;
+
+ /**
+ * Constructor.
+ *
+ * You have to call acquire() to actually acquire the lock.
+ *
+ * @param string $path Path to the file to which access is controlled
+ */
+ public function __construct($path)
+ {
+ $this->path = $path;
+ $this->lock_fp = null;
+ }
+
+ /**
+ * Tries to acquire the lock.
+ *
+ * If the lock is already held by another process, this call will block
+ * until the other process releases the lock. If a lock is acquired and
+ * is not released before script finishes but the process continues to
+ * live (apache/fastcgi) then subsequent processes trying to acquire
+ * the same lock will be blocked forever.
+ *
+ * If the lock is already held by the same process via another instance
+ * of this class, this call will block forever.
+ *
+ * If flock function is disabled in php or fails to work, lock
+ * acquisition will fail and false will be returned.
+ *
+ * @return bool true if lock was acquired
+ * false otherwise
+ */
+ public function acquire()
+ {
+ if ($this->lock_fp)
+ {
+ return false;
+ }
+
+ // For systems that can't have two processes opening
+ // one file for writing simultaneously
+ if (file_exists($this->path . '.lock'))
+ {
+ $mode = 'rb';
+ }
+ else
+ {
+ $mode = 'wb';
+ }
+
+ $this->lock_fp = @fopen($this->path . '.lock', $mode);
+
+ if ($mode == 'wb')
+ {
+ if (!$this->lock_fp)
+ {
+ // Two processes may attempt to create lock file at the same time.
+ // Have the losing process try opening the lock file again for reading
+ // on the assumption that the winning process created it
+ $mode = 'rb';
+ $this->lock_fp = @fopen($this->path . '.lock', $mode);
+ }
+ else
+ {
+ // Only need to set mode when the lock file is written
+ @chmod($this->path . '.lock', 0666);
+ }
+ }
+
+ if ($this->lock_fp)
+ {
+ @flock($this->lock_fp, LOCK_EX);
+ }
+
+ return (bool) $this->lock_fp;
+ }
+
+ /**
+ * Releases the lock.
+ *
+ * The lock must have been previously obtained, that is, acquire() call
+ * was issued and returned true.
+ *
+ * Note: Attempting to release a lock that is already released,
+ * that is, calling release() multiple times, is harmless.
+ *
+ * @return null
+ */
+ public function release()
+ {
+ if ($this->lock_fp)
+ {
+ @flock($this->lock_fp, LOCK_UN);
+ fclose($this->lock_fp);
+ $this->lock_fp = null;
+ }
+ }
+}
diff --git a/phpBB/includes/mcp/mcp_forum.php b/phpBB/includes/mcp/mcp_forum.php
index 4dd5e5856a..0fad9e2a22 100644
--- a/phpBB/includes/mcp/mcp_forum.php
+++ b/phpBB/includes/mcp/mcp_forum.php
@@ -430,13 +430,16 @@ function merge_topics($forum_id, $topic_ids, $to_topic_id)
// Message and return links
$success_msg = 'POSTS_MERGED_SUCCESS';
- // If the topic no longer exist, we will update the topic watch table.
- // To not let it error out on users watching both topics, we just return on an error...
- $db->sql_return_on_error(true);
- $db->sql_query('UPDATE ' . TOPICS_WATCH_TABLE . ' SET topic_id = ' . (int) $to_topic_id . ' WHERE ' . $db->sql_in_set('topic_id', $topic_ids));
- $db->sql_return_on_error(false);
+ if (!function_exists('phpbb_update_rows_avoiding_duplicates_notify_status'))
+ {
+ include($phpbb_root_path . 'includes/functions_database_helper.' . $phpEx);
+ }
+
+ // Update the topic watch table.
+ phpbb_update_rows_avoiding_duplicates_notify_status($db, TOPICS_WATCH_TABLE, 'topic_id', $topic_ids, $to_topic_id);
- $db->sql_query('DELETE FROM ' . TOPICS_WATCH_TABLE . ' WHERE ' . $db->sql_in_set('topic_id', $topic_ids));
+ // Update the bookmarks table.
+ phpbb_update_rows_avoiding_duplicates($db, BOOKMARKS_TABLE, 'topic_id', $topic_ids, $to_topic_id);
// Link to the new topic
$return_link .= (($return_link) ? '<br /><br />' : '') . sprintf($user->lang['RETURN_NEW_TOPIC'], '<a href="' . append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'f=' . $to_forum_id . '&amp;t=' . $to_topic_id) . '">', '</a>');
diff --git a/phpBB/includes/mcp/mcp_main.php b/phpBB/includes/mcp/mcp_main.php
index 95ca7c2e1b..c28b466bcf 100644
--- a/phpBB/includes/mcp/mcp_main.php
+++ b/phpBB/includes/mcp/mcp_main.php
@@ -1080,6 +1080,7 @@ function mcp_fork_topic($topic_ids)
}
}
+ // Copy topic subscriptions to new topic
$sql = 'SELECT user_id, notify_status
FROM ' . TOPICS_WATCH_TABLE . '
WHERE topic_id = ' . $topic_id;
@@ -1100,6 +1101,27 @@ function mcp_fork_topic($topic_ids)
{
$db->sql_multi_insert(TOPICS_WATCH_TABLE, $sql_ary);
}
+
+ // Copy bookmarks to new topic
+ $sql = 'SELECT user_id
+ FROM ' . BOOKMARKS_TABLE . '
+ WHERE topic_id = ' . $topic_id;
+ $result = $db->sql_query($sql);
+
+ $sql_ary = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $sql_ary[] = array(
+ 'topic_id' => (int) $new_topic_id,
+ 'user_id' => (int) $row['user_id'],
+ );
+ }
+ $db->sql_freeresult($result);
+
+ if (sizeof($sql_ary))
+ {
+ $db->sql_multi_insert(BOOKMARKS_TABLE, $sql_ary);
+ }
}
// Sync new topics, parent forums and board stats
diff --git a/phpBB/includes/mcp/mcp_pm_reports.php b/phpBB/includes/mcp/mcp_pm_reports.php
index be18dba944..86650947c7 100644
--- a/phpBB/includes/mcp/mcp_pm_reports.php
+++ b/phpBB/includes/mcp/mcp_pm_reports.php
@@ -122,6 +122,7 @@ class mcp_pm_reports
$message = bbcode_nl2br($message);
$message = smiley_text($message);
+ $report['report_text'] = make_clickable(bbcode_nl2br($report['report_text']));
if ($pm_info['message_attachment'] && $auth->acl_get('u_pm_download'))
{
diff --git a/phpBB/includes/mcp/mcp_reports.php b/phpBB/includes/mcp/mcp_reports.php
index 3426d62cdb..8da303f6e3 100644
--- a/phpBB/includes/mcp/mcp_reports.php
+++ b/phpBB/includes/mcp/mcp_reports.php
@@ -71,7 +71,7 @@ class mcp_reports
// closed reports are accessed by report id
$report_id = request_var('r', 0);
- $sql = 'SELECT r.post_id, r.user_id, r.report_id, r.report_closed, report_time, r.report_text, r.reported_post_text, r.reported_post_uid, r.reported_post_bitfield, rr.reason_title, rr.reason_description, u.username, u.username_clean, u.user_colour
+ $sql = 'SELECT r.post_id, r.user_id, r.report_id, r.report_closed, report_time, r.report_text, r.reported_post_text, r.reported_post_uid, r.reported_post_bitfield, r.reported_post_enable_magic_url, r.reported_post_enable_smilies, r.reported_post_enable_bbcode, rr.reason_title, rr.reason_description, u.username, u.username_clean, u.user_colour
FROM ' . REPORTS_TABLE . ' r, ' . REPORTS_REASONS_TABLE . ' rr, ' . USERS_TABLE . ' u
WHERE ' . (($report_id) ? 'r.report_id = ' . $report_id : "r.post_id = $post_id") . '
AND rr.reason_id = r.reason_id
@@ -94,6 +94,10 @@ class mcp_reports
$post_id = $report['post_id'];
$report_id = $report['report_id'];
+
+ $parse_post_flags = $report['reported_post_enable_bbcode'] ? OPTION_FLAG_BBCODE : 0;
+ $parse_post_flags += $report['reported_post_enable_smilies'] ? OPTION_FLAG_SMILIES : 0;
+ $parse_post_flags += $report['reported_post_enable_magic_url'] ? OPTION_FLAG_LINKS : 0;
$post_info = get_post_data(array($post_id), 'm_report', true);
@@ -136,18 +140,7 @@ class mcp_reports
$post_unread = (isset($topic_tracking_info[$post_info['topic_id']]) && $post_info['post_time'] > $topic_tracking_info[$post_info['topic_id']]) ? true : false;
- // Process message, leave it uncensored
- $message = $post_info['post_text'];
- if ($post_info['bbcode_bitfield'])
- {
- include_once($phpbb_root_path . 'includes/bbcode.' . $phpEx);
- $bbcode = new bbcode($post_info['bbcode_bitfield']);
- $bbcode->bbcode_second_pass($message, $post_info['bbcode_uid'], $post_info['bbcode_bitfield']);
- }
-
- $message = bbcode_nl2br($message);
- $message = smiley_text($message);
$report['report_text'] = make_clickable(bbcode_nl2br($report['report_text']));
if ($post_info['post_attachment'] && $auth->acl_get('u_download') && $auth->acl_get('f_download', $post_info['forum_id']))
@@ -168,7 +161,7 @@ class mcp_reports
if (sizeof($attachments))
{
$update_count = array();
- parse_attachments($post_info['forum_id'], $message, $attachments, $update_count);
+ parse_attachments($post_info['forum_id'], $report['reported_post_text'], $attachments, $update_count);
}
// Display not already displayed Attachments for this post, we already parsed them. ;)
@@ -227,7 +220,7 @@ class mcp_reports
'REPORTER_NAME' => get_username_string('username', $report['user_id'], $report['username'], $report['user_colour']),
'U_VIEW_REPORTER_PROFILE' => get_username_string('profile', $report['user_id'], $report['username'], $report['user_colour']),
- 'POST_PREVIEW' => generate_text_for_display($report['reported_post_text'], $report['reported_post_uid'], $report['reported_post_bitfield'], OPTION_FLAG_BBCODE | OPTION_FLAG_SMILIES, false),
+ 'POST_PREVIEW' => generate_text_for_display($report['reported_post_text'], $report['reported_post_uid'], $report['reported_post_bitfield'], $parse_post_flags, false),
'POST_SUBJECT' => ($post_info['post_subject']) ? $post_info['post_subject'] : $user->lang['NO_SUBJECT'],
'POST_DATE' => $user->format_date($post_info['post_time']),
'POST_IP' => $post_info['poster_ip'],
diff --git a/phpBB/includes/mcp/mcp_topic.php b/phpBB/includes/mcp/mcp_topic.php
index 63ff7bed72..e3dd5a6b57 100644
--- a/phpBB/includes/mcp/mcp_topic.php
+++ b/phpBB/includes/mcp/mcp_topic.php
@@ -521,6 +521,49 @@ function split_topic($action, $topic_id, $to_forum_id, $subject)
WHERE post_id = {$post_id_list[0]}";
$db->sql_query($sql);
+ // Copy topic subscriptions to new topic
+ $sql = 'SELECT user_id, notify_status
+ FROM ' . TOPICS_WATCH_TABLE . '
+ WHERE topic_id = ' . $topic_id;
+ $result = $db->sql_query($sql);
+
+ $sql_ary = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $sql_ary[] = array(
+ 'topic_id' => (int) $to_topic_id,
+ 'user_id' => (int) $row['user_id'],
+ 'notify_status' => (int) $row['notify_status'],
+ );
+ }
+ $db->sql_freeresult($result);
+
+ if (sizeof($sql_ary))
+ {
+ $db->sql_multi_insert(TOPICS_WATCH_TABLE, $sql_ary);
+ }
+
+ // Copy bookmarks to new topic
+ $sql = 'SELECT user_id
+ FROM ' . BOOKMARKS_TABLE . '
+ WHERE topic_id = ' . $topic_id;
+ $result = $db->sql_query($sql);
+
+ $sql_ary = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $sql_ary[] = array(
+ 'topic_id' => (int) $to_topic_id,
+ 'user_id' => (int) $row['user_id'],
+ );
+ }
+ $db->sql_freeresult($result);
+
+ if (sizeof($sql_ary))
+ {
+ $db->sql_multi_insert(BOOKMARKS_TABLE, $sql_ary);
+ }
+
$success_msg = 'TOPIC_SPLIT_SUCCESS';
// Update forum statistics
@@ -623,13 +666,16 @@ function merge_posts($topic_id, $to_topic_id)
}
else
{
+ if (!function_exists('phpbb_update_rows_avoiding_duplicates_notify_status'))
+ {
+ include($phpbb_root_path . 'includes/functions_database_helper.' . $phpEx);
+ }
+
// If the topic no longer exist, we will update the topic watch table.
- // To not let it error out on users watching both topics, we just return on an error...
- $db->sql_return_on_error(true);
- $db->sql_query('UPDATE ' . TOPICS_WATCH_TABLE . ' SET topic_id = ' . (int) $to_topic_id . ' WHERE topic_id = ' . (int) $topic_id);
- $db->sql_return_on_error(false);
+ phpbb_update_rows_avoiding_duplicates_notify_status($db, TOPICS_WATCH_TABLE, 'topic_id', $topic_ids, $to_topic_id);
- $db->sql_query('DELETE FROM ' . TOPICS_WATCH_TABLE . ' WHERE topic_id = ' . (int) $topic_id);
+ // If the topic no longer exist, we will update the bookmarks table.
+ phpbb_update_rows_avoiding_duplicates($db, BOOKMARKS_TABLE, 'topic_id', $topic_id, $to_topic_id);
}
// Link to the new topic
diff --git a/phpBB/includes/message_parser.php b/phpBB/includes/message_parser.php
index 1cd2a46fa1..44960dd78d 100644
--- a/phpBB/includes/message_parser.php
+++ b/phpBB/includes/message_parser.php
@@ -702,17 +702,6 @@ class bbcode_firstpass extends bbcode
{
global $config, $user;
- /**
- * If you change this code, make sure the cases described within the following reports are still working:
- * #3572 - [quote="[test]test"]test [ test[/quote] - (correct: parsed)
- * #14667 - [quote]test[/quote] test ] and [ test [quote]test[/quote] (correct: parsed)
- * #14770 - [quote="["]test[/quote] (correct: parsed)
- * [quote="[i]test[/i]"]test[/quote] (correct: parsed)
- * [quote="[quote]test[/quote]"]test[/quote] (correct: parsed - Username displayed as [quote]test[/quote])
- * #20735 - [quote]test[/[/b]quote] test [/quote][/quote] test - (correct: quoted: "test[/[/b]quote] test" / non-quoted: "[/quote] test" - also failed if layout distorted)
- * #40565 - [quote="a"]a[/quote][quote="a]a[/quote] (correct: first quote tag parsed, second quote tag unparsed)
- */
-
$in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in)));
if (!$in)
diff --git a/phpBB/includes/questionnaire/questionnaire.php b/phpBB/includes/questionnaire/questionnaire.php
index f0fb8c3c06..6f2727a38c 100644
--- a/phpBB/includes/questionnaire/questionnaire.php
+++ b/phpBB/includes/questionnaire/questionnaire.php
@@ -70,7 +70,7 @@ class phpbb_questionnaire_data_collector
/**
* Collect info into the data property.
*
- * @return void
+ * @return null
*/
function collect()
{
@@ -260,6 +260,8 @@ class phpbb_questionnaire_phpbb_data_provider
include("{$phpbb_root_path}config.$phpEx");
unset($dbhost, $dbport, $dbname, $dbuser, $dbpasswd); // Just a precaution
+ $dbms = phpbb_convert_30_dbms_to_31($dbms);
+
// Only send certain config vars
$config_vars = array(
'active_sessions' => true,
diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php
index 58a4dd7d6a..324c214e91 100644
--- a/phpBB/includes/search/fulltext_mysql.php
+++ b/phpBB/includes/search/fulltext_mysql.php
@@ -41,8 +41,8 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
protected $config;
/**
- * DBAL object
- * @var dbal
+ * Database connection
+ * @var phpbb_db_driver
*/
protected $db;
@@ -86,6 +86,14 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$this->word_length = array('min' => $this->config['fulltext_mysql_min_word_len'], 'max' => $this->config['fulltext_mysql_max_word_len']);
+ /**
+ * Load the UTF tools
+ */
+ if (!function_exists('utf8_strlen'))
+ {
+ include($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
+ }
+
$error = false;
}
@@ -230,7 +238,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
}
else
{
- $tmp_split_words[] = $word . ' ';
+ $tmp_split_words[] = $word;
}
}
if ($phrase)
diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php
index 4623326fc7..53df8348ae 100644
--- a/phpBB/includes/search/fulltext_native.php
+++ b/phpBB/includes/search/fulltext_native.php
@@ -85,8 +85,8 @@ class phpbb_search_fulltext_native extends phpbb_search_base
protected $config;
/**
- * DBAL object
- * @var dbal
+ * Database connection
+ * @var phpbb_db_driver
*/
protected $db;
diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php
index 08f64735b6..1475cc31d0 100644
--- a/phpBB/includes/search/fulltext_postgres.php
+++ b/phpBB/includes/search/fulltext_postgres.php
@@ -66,8 +66,8 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
protected $config;
/**
- * DBAL object
- * @var dbal
+ * Database connection
+ * @var phpbb_db_driver
*/
protected $db;
@@ -121,6 +121,14 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
}
}
+ /**
+ * Load the UTF tools
+ */
+ if (!function_exists('utf8_strlen'))
+ {
+ include($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
+ }
+
$error = false;
}
@@ -468,10 +476,14 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$tmp_sql_match[] = "to_tsvector ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', " . $sql_match_column . ") @@ to_tsquery ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', '" . $this->db->sql_escape($this->tsearch_query) . "')";
}
+ $this->db->sql_transaction('begin');
+
+ $sql_from = "FROM $sql_from$sql_sort_table" . POSTS_TABLE . " p";
+ $sql_where = "WHERE (" . implode(' OR ', $tmp_sql_match) . ")
+ $sql_where_options";
$sql = "SELECT $sql_select
- FROM $sql_from$sql_sort_table" . POSTS_TABLE . " p
- WHERE (" . implode(' OR ', $tmp_sql_match) . ")
- $sql_where_options
+ $sql_from
+ $sql_where
ORDER BY $sql_sort";
$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
@@ -491,7 +503,12 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
// if the total result count is not cached yet, retrieve it from the db
if (!$result_count)
{
- $result_count = sizeof ($id_ary);
+ $sql_count = "SELECT COUNT(*) as result_count
+ $sql_from
+ $sql_where";
+ $result = $this->db->sql_query($sql_count);
+ $result_count = (int) $this->db->sql_fetchfield('result_count');
+ $this->db->sql_freeresult($result);
if (!$result_count)
{
@@ -499,6 +516,8 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
}
}
+ $this->db->sql_transaction('commit');
+
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@@ -639,6 +658,8 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$field = 'topic_id';
}
+ $this->db->sql_transaction('begin');
+
// Only read one block of posts from the db and then cache it
$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
@@ -651,7 +672,35 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
// retrieve the total result count if needed
if (!$result_count)
{
- $result_count = sizeof ($id_ary);
+ if ($type == 'posts')
+ {
+ $sql_count = "SELECT COUNT(*) as result_count
+ FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (($firstpost_only) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
+ WHERE $sql_author
+ $sql_topic_id
+ $sql_firstpost
+ $m_approve_fid_sql
+ $sql_fora
+ $sql_sort_join
+ $sql_time";
+ }
+ else
+ {
+ $sql_count = "SELECT COUNT(*) as result_count
+ FROM " . $sql_sort_table . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
+ WHERE $sql_author
+ $sql_topic_id
+ $sql_firstpost
+ $m_approve_fid_sql
+ $sql_fora
+ AND t.topic_id = p.topic_id
+ $sql_sort_join
+ $sql_time
+ GROUP BY t.topic_id, $sort_by_sql[$sort_key]";
+ }
+
+ $result = $this->db->sql_query($sql_count);
+ $result_count = (int) $this->db->sql_fetchfield('result_count');
if (!$result_count)
{
@@ -659,6 +708,8 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
}
}
+ $this->db->sql_transaction('commit');
+
if (sizeof($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
diff --git a/phpBB/includes/search/fulltext_sphinx.php b/phpBB/includes/search/fulltext_sphinx.php
index dd5634b623..4bacf74f93 100644
--- a/phpBB/includes/search/fulltext_sphinx.php
+++ b/phpBB/includes/search/fulltext_sphinx.php
@@ -84,8 +84,8 @@ class phpbb_search_fulltext_sphinx
protected $config;
/**
- * DBAL object
- * @var dbal
+ * Database connection
+ * @var phpbb_db_driver
*/
protected $db;
@@ -875,7 +875,7 @@ class phpbb_search_fulltext_sphinx
</dl>
<dl>
<dt><label for="fulltext_sphinx_indexer_mem_limit">' . $this->user->lang['FULLTEXT_SPHINX_INDEXER_MEM_LIMIT'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_SPHINX_INDEXER_MEM_LIMIT_EXPLAIN'] . '</span></dt>
- <dd><input id="fulltext_sphinx_indexer_mem_limit" type="text" size="4" maxlength="10" name="config[fulltext_sphinx_indexer_mem_limit]" value="' . $this->config['fulltext_sphinx_indexer_mem_limit'] . '" />' . $this->user->lang['MIB'] . '</dd>
+ <dd><input id="fulltext_sphinx_indexer_mem_limit" type="text" size="4" maxlength="10" name="config[fulltext_sphinx_indexer_mem_limit]" value="' . $this->config['fulltext_sphinx_indexer_mem_limit'] . '" /> ' . $this->user->lang['MIB'] . '</dd>
</dl>
<dl>
<dt><label for="fulltext_sphinx_config_file">' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '</dt>
diff --git a/phpBB/includes/sphinxapi.php b/phpBB/includes/sphinxapi.php
index bd83b1d2e0..6c3b66710c 100644
--- a/phpBB/includes/sphinxapi.php
+++ b/phpBB/includes/sphinxapi.php
@@ -1,1712 +1,1712 @@
-<?php
-
-//
-// $Id: sphinxapi.php 3087 2012-01-30 23:07:35Z shodan $
-//
-
-//
-// Copyright (c) 2001-2012, Andrew Aksyonoff
-// Copyright (c) 2008-2012, Sphinx Technologies Inc
-// All rights reserved
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License. You should have
-// received a copy of the GPL license along with this program; if you
-// did not, you can find it at http://www.gnu.org/
-//
-
-/////////////////////////////////////////////////////////////////////////////
-// PHP version of Sphinx searchd client (PHP API)
-/////////////////////////////////////////////////////////////////////////////
-
-/// known searchd commands
-define ( "SEARCHD_COMMAND_SEARCH", 0 );
-define ( "SEARCHD_COMMAND_EXCERPT", 1 );
-define ( "SEARCHD_COMMAND_UPDATE", 2 );
-define ( "SEARCHD_COMMAND_KEYWORDS", 3 );
-define ( "SEARCHD_COMMAND_PERSIST", 4 );
-define ( "SEARCHD_COMMAND_STATUS", 5 );
-define ( "SEARCHD_COMMAND_FLUSHATTRS", 7 );
-
-/// current client-side command implementation versions
-define ( "VER_COMMAND_SEARCH", 0x119 );
-define ( "VER_COMMAND_EXCERPT", 0x104 );
-define ( "VER_COMMAND_UPDATE", 0x102 );
-define ( "VER_COMMAND_KEYWORDS", 0x100 );
-define ( "VER_COMMAND_STATUS", 0x100 );
-define ( "VER_COMMAND_QUERY", 0x100 );
-define ( "VER_COMMAND_FLUSHATTRS", 0x100 );
-
-/// known searchd status codes
-define ( "SEARCHD_OK", 0 );
-define ( "SEARCHD_ERROR", 1 );
-define ( "SEARCHD_RETRY", 2 );
-define ( "SEARCHD_WARNING", 3 );
-
-/// known match modes
-define ( "SPH_MATCH_ALL", 0 );
-define ( "SPH_MATCH_ANY", 1 );
-define ( "SPH_MATCH_PHRASE", 2 );
-define ( "SPH_MATCH_BOOLEAN", 3 );
-define ( "SPH_MATCH_EXTENDED", 4 );
-define ( "SPH_MATCH_FULLSCAN", 5 );
-define ( "SPH_MATCH_EXTENDED2", 6 ); // extended engine V2 (TEMPORARY, WILL BE REMOVED)
-
-/// known ranking modes (ext2 only)
-define ( "SPH_RANK_PROXIMITY_BM25", 0 ); ///< default mode, phrase proximity major factor and BM25 minor one
-define ( "SPH_RANK_BM25", 1 ); ///< statistical mode, BM25 ranking only (faster but worse quality)
-define ( "SPH_RANK_NONE", 2 ); ///< no ranking, all matches get a weight of 1
-define ( "SPH_RANK_WORDCOUNT", 3 ); ///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
-define ( "SPH_RANK_PROXIMITY", 4 );
-define ( "SPH_RANK_MATCHANY", 5 );
-define ( "SPH_RANK_FIELDMASK", 6 );
-define ( "SPH_RANK_SPH04", 7 );
-define ( "SPH_RANK_EXPR", 8 );
-define ( "SPH_RANK_TOTAL", 9 );
-
-/// known sort modes
-define ( "SPH_SORT_RELEVANCE", 0 );
-define ( "SPH_SORT_ATTR_DESC", 1 );
-define ( "SPH_SORT_ATTR_ASC", 2 );
-define ( "SPH_SORT_TIME_SEGMENTS", 3 );
-define ( "SPH_SORT_EXTENDED", 4 );
-define ( "SPH_SORT_EXPR", 5 );
-
-/// known filter types
-define ( "SPH_FILTER_VALUES", 0 );
-define ( "SPH_FILTER_RANGE", 1 );
-define ( "SPH_FILTER_FLOATRANGE", 2 );
-
-/// known attribute types
-define ( "SPH_ATTR_INTEGER", 1 );
-define ( "SPH_ATTR_TIMESTAMP", 2 );
-define ( "SPH_ATTR_ORDINAL", 3 );
-define ( "SPH_ATTR_BOOL", 4 );
-define ( "SPH_ATTR_FLOAT", 5 );
-define ( "SPH_ATTR_BIGINT", 6 );
-define ( "SPH_ATTR_STRING", 7 );
-define ( "SPH_ATTR_MULTI", 0x40000001 );
-define ( "SPH_ATTR_MULTI64", 0x40000002 );
-
-/// known grouping functions
-define ( "SPH_GROUPBY_DAY", 0 );
-define ( "SPH_GROUPBY_WEEK", 1 );
-define ( "SPH_GROUPBY_MONTH", 2 );
-define ( "SPH_GROUPBY_YEAR", 3 );
-define ( "SPH_GROUPBY_ATTR", 4 );
-define ( "SPH_GROUPBY_ATTRPAIR", 5 );
-
-// important properties of PHP's integers:
-// - always signed (one bit short of PHP_INT_SIZE)
-// - conversion from string to int is saturated
-// - float is double
-// - div converts arguments to floats
-// - mod converts arguments to ints
-
-// the packing code below works as follows:
-// - when we got an int, just pack it
-// if performance is a problem, this is the branch users should aim for
-//
-// - otherwise, we got a number in string form
-// this might be due to different reasons, but we assume that this is
-// because it didn't fit into PHP int
-//
-// - factor the string into high and low ints for packing
-// - if we have bcmath, then it is used
-// - if we don't, we have to do it manually (this is the fun part)
-//
-// - x64 branch does factoring using ints
-// - x32 (ab)uses floats, since we can't fit unsigned 32-bit number into an int
-//
-// unpacking routines are pretty much the same.
-// - return ints if we can
-// - otherwise format number into a string
-
-/// pack 64-bit signed
-function sphPackI64 ( $v )
-{
- assert ( is_numeric($v) );
-
- // x64
- if ( PHP_INT_SIZE>=8 )
- {
- $v = (int)$v;
- return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
- }
-
- // x32, int
- if ( is_int($v) )
- return pack ( "NN", $v < 0 ? -1 : 0, $v );
-
- // x32, bcmath
- if ( function_exists("bcmul") )
- {
- if ( bccomp ( $v, 0 ) == -1 )
- $v = bcadd ( "18446744073709551616", $v );
- $h = bcdiv ( $v, "4294967296", 0 );
- $l = bcmod ( $v, "4294967296" );
- return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
- }
-
- // x32, no-bcmath
- $p = max(0, strlen($v) - 13);
- $lo = abs((float)substr($v, $p));
- $hi = abs((float)substr($v, 0, $p));
-
- $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
- $q = floor($m/4294967296.0);
- $l = $m - ($q*4294967296.0);
- $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
-
- if ( $v<0 )
- {
- if ( $l==0 )
- $h = 4294967296.0 - $h;
- else
- {
- $h = 4294967295.0 - $h;
- $l = 4294967296.0 - $l;
- }
- }
- return pack ( "NN", $h, $l );
-}
-
-/// pack 64-bit unsigned
-function sphPackU64 ( $v )
-{
- assert ( is_numeric($v) );
-
- // x64
- if ( PHP_INT_SIZE>=8 )
- {
- assert ( $v>=0 );
-
- // x64, int
- if ( is_int($v) )
- return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
-
- // x64, bcmath
- if ( function_exists("bcmul") )
- {
- $h = bcdiv ( $v, 4294967296, 0 );
- $l = bcmod ( $v, 4294967296 );
- return pack ( "NN", $h, $l );
- }
-
- // x64, no-bcmath
- $p = max ( 0, strlen($v) - 13 );
- $lo = (int)substr ( $v, $p );
- $hi = (int)substr ( $v, 0, $p );
-
- $m = $lo + $hi*1316134912;
- $l = $m % 4294967296;
- $h = $hi*2328 + (int)($m/4294967296);
-
- return pack ( "NN", $h, $l );
- }
-
- // x32, int
- if ( is_int($v) )
- return pack ( "NN", 0, $v );
-
- // x32, bcmath
- if ( function_exists("bcmul") )
- {
- $h = bcdiv ( $v, "4294967296", 0 );
- $l = bcmod ( $v, "4294967296" );
- return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
- }
-
- // x32, no-bcmath
- $p = max(0, strlen($v) - 13);
- $lo = (float)substr($v, $p);
- $hi = (float)substr($v, 0, $p);
-
- $m = $lo + $hi*1316134912.0;
- $q = floor($m / 4294967296.0);
- $l = $m - ($q * 4294967296.0);
- $h = $hi*2328.0 + $q;
-
- return pack ( "NN", $h, $l );
-}
-
-// unpack 64-bit unsigned
-function sphUnpackU64 ( $v )
-{
- list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
-
- if ( PHP_INT_SIZE>=8 )
- {
- if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
- if ( $lo<0 ) $lo += (1<<32);
-
- // x64, int
- if ( $hi<=2147483647 )
- return ($hi<<32) + $lo;
-
- // x64, bcmath
- if ( function_exists("bcmul") )
- return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
-
- // x64, no-bcmath
- $C = 100000;
- $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
- $l = (($hi % $C) << 32) + ($lo % $C);
- if ( $l>$C )
- {
- $h += (int)($l / $C);
- $l = $l % $C;
- }
-
- if ( $h==0 )
- return $l;
- return sprintf ( "%d%05d", $h, $l );
- }
-
- // x32, int
- if ( $hi==0 )
- {
- if ( $lo>0 )
- return $lo;
- return sprintf ( "%u", $lo );
- }
-
- $hi = sprintf ( "%u", $hi );
- $lo = sprintf ( "%u", $lo );
-
- // x32, bcmath
- if ( function_exists("bcmul") )
- return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
-
- // x32, no-bcmath
- $hi = (float)$hi;
- $lo = (float)$lo;
-
- $q = floor($hi/10000000.0);
- $r = $hi - $q*10000000.0;
- $m = $lo + $r*4967296.0;
- $mq = floor($m/10000000.0);
- $l = $m - $mq*10000000.0;
- $h = $q*4294967296.0 + $r*429.0 + $mq;
-
- $h = sprintf ( "%.0f", $h );
- $l = sprintf ( "%07.0f", $l );
- if ( $h=="0" )
- return sprintf( "%.0f", (float)$l );
- return $h . $l;
-}
-
-// unpack 64-bit signed
-function sphUnpackI64 ( $v )
-{
- list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
-
- // x64
- if ( PHP_INT_SIZE>=8 )
- {
- if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
- if ( $lo<0 ) $lo += (1<<32);
-
- return ($hi<<32) + $lo;
- }
-
- // x32, int
- if ( $hi==0 )
- {
- if ( $lo>0 )
- return $lo;
- return sprintf ( "%u", $lo );
- }
- // x32, int
- elseif ( $hi==-1 )
- {
- if ( $lo<0 )
- return $lo;
- return sprintf ( "%.0f", $lo - 4294967296.0 );
- }
-
- $neg = "";
- $c = 0;
- if ( $hi<0 )
- {
- $hi = ~$hi;
- $lo = ~$lo;
- $c = 1;
- $neg = "-";
- }
-
- $hi = sprintf ( "%u", $hi );
- $lo = sprintf ( "%u", $lo );
-
- // x32, bcmath
- if ( function_exists("bcmul") )
- return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c );
-
- // x32, no-bcmath
- $hi = (float)$hi;
- $lo = (float)$lo;
-
- $q = floor($hi/10000000.0);
- $r = $hi - $q*10000000.0;
- $m = $lo + $r*4967296.0;
- $mq = floor($m/10000000.0);
- $l = $m - $mq*10000000.0 + $c;
- $h = $q*4294967296.0 + $r*429.0 + $mq;
- if ( $l==10000000 )
- {
- $l = 0;
- $h += 1;
- }
-
- $h = sprintf ( "%.0f", $h );
- $l = sprintf ( "%07.0f", $l );
- if ( $h=="0" )
- return $neg . sprintf( "%.0f", (float)$l );
- return $neg . $h . $l;
-}
-
-
-function sphFixUint ( $value )
-{
- if ( PHP_INT_SIZE>=8 )
- {
- // x64 route, workaround broken unpack() in 5.2.2+
- if ( $value<0 ) $value += (1<<32);
- return $value;
- }
- else
- {
- // x32 route, workaround php signed/unsigned braindamage
- return sprintf ( "%u", $value );
- }
-}
-
-
-/// sphinx searchd client class
-class SphinxClient
-{
- var $_host; ///< searchd host (default is "localhost")
- var $_port; ///< searchd port (default is 9312)
- var $_offset; ///< how many records to seek from result-set start (default is 0)
- var $_limit; ///< how many records to return from result-set starting at offset (default is 20)
- var $_mode; ///< query matching mode (default is SPH_MATCH_ALL)
- var $_weights; ///< per-field weights (default is 1 for all fields)
- var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
- var $_sortby; ///< attribute to sort by (defualt is "")
- var $_min_id; ///< min ID to match (default is 0, which means no limit)
- var $_max_id; ///< max ID to match (default is 0, which means no limit)
- var $_filters; ///< search filters
- var $_groupby; ///< group-by attribute name
- var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with)
- var $_groupsort; ///< group-by sorting clause (to sort groups in result set with)
- var $_groupdistinct;///< group-by count-distinct attribute
- var $_maxmatches; ///< max matches to retrieve
- var $_cutoff; ///< cutoff to stop searching at (default is 0)
- var $_retrycount; ///< distributed retries count
- var $_retrydelay; ///< distributed retries delay
- var $_anchor; ///< geographical anchor point
- var $_indexweights; ///< per-index weights
- var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
- var $_rankexpr; ///< ranking mode expression (for SPH_RANK_EXPR)
- var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit)
- var $_fieldweights; ///< per-field-name weights
- var $_overrides; ///< per-query attribute values overrides
- var $_select; ///< select-list (attributes or expressions, with optional aliases)
-
- var $_error; ///< last error message
- var $_warning; ///< last warning message
- var $_connerror; ///< connection error vs remote error flag
-
- var $_reqs; ///< requests array for multi-query
- var $_mbenc; ///< stored mbstring encoding
- var $_arrayresult; ///< whether $result["matches"] should be a hash or an array
- var $_timeout; ///< connect timeout
-
- /////////////////////////////////////////////////////////////////////////////
- // common stuff
- /////////////////////////////////////////////////////////////////////////////
-
- /// create a new client object and fill defaults
- function SphinxClient ()
- {
- // per-client-object settings
- $this->_host = "localhost";
- $this->_port = 9312;
- $this->_path = false;
- $this->_socket = false;
-
- // per-query settings
- $this->_offset = 0;
- $this->_limit = 20;
- $this->_mode = SPH_MATCH_ALL;
- $this->_weights = array ();
- $this->_sort = SPH_SORT_RELEVANCE;
- $this->_sortby = "";
- $this->_min_id = 0;
- $this->_max_id = 0;
- $this->_filters = array ();
- $this->_groupby = "";
- $this->_groupfunc = SPH_GROUPBY_DAY;
- $this->_groupsort = "@group desc";
- $this->_groupdistinct= "";
- $this->_maxmatches = 1000;
- $this->_cutoff = 0;
- $this->_retrycount = 0;
- $this->_retrydelay = 0;
- $this->_anchor = array ();
- $this->_indexweights= array ();
- $this->_ranker = SPH_RANK_PROXIMITY_BM25;
- $this->_rankexpr = "";
- $this->_maxquerytime= 0;
- $this->_fieldweights= array();
- $this->_overrides = array();
- $this->_select = "*";
-
- $this->_error = ""; // per-reply fields (for single-query case)
- $this->_warning = "";
- $this->_connerror = false;
-
- $this->_reqs = array (); // requests storage (for multi-query case)
- $this->_mbenc = "";
- $this->_arrayresult = false;
- $this->_timeout = 0;
- }
-
- function __destruct()
- {
- if ( $this->_socket !== false )
- fclose ( $this->_socket );
- }
-
- /// get last error message (string)
- function GetLastError ()
- {
- return $this->_error;
- }
-
- /// get last warning message (string)
- function GetLastWarning ()
- {
- return $this->_warning;
- }
-
- /// get last error flag (to tell network connection errors from searchd errors or broken responses)
- function IsConnectError()
- {
- return $this->_connerror;
- }
-
- /// set searchd host name (string) and port (integer)
- function SetServer ( $host, $port = 0 )
- {
- assert ( is_string($host) );
- if ( $host[0] == '/')
- {
- $this->_path = 'unix://' . $host;
- return;
- }
- if ( substr ( $host, 0, 7 )=="unix://" )
- {
- $this->_path = $host;
- return;
- }
-
- assert ( is_int($port) );
- $this->_host = $host;
- $this->_port = $port;
- $this->_path = '';
-
- }
-
- /// set server connection timeout (0 to remove)
- function SetConnectTimeout ( $timeout )
- {
- assert ( is_numeric($timeout) );
- $this->_timeout = $timeout;
- }
-
-
- function _Send ( $handle, $data, $length )
- {
- if ( feof($handle) || fwrite ( $handle, $data, $length ) !== $length )
- {
- $this->_error = 'connection unexpectedly closed (timed out?)';
- $this->_connerror = true;
- return false;
- }
- return true;
- }
-
- /////////////////////////////////////////////////////////////////////////////
-
- /// enter mbstring workaround mode
- function _MBPush ()
- {
- $this->_mbenc = "";
- if ( ini_get ( "mbstring.func_overload" ) & 2 )
- {
- $this->_mbenc = mb_internal_encoding();
- mb_internal_encoding ( "latin1" );
- }
- }
-
- /// leave mbstring workaround mode
- function _MBPop ()
- {
- if ( $this->_mbenc )
- mb_internal_encoding ( $this->_mbenc );
- }
-
- /// connect to searchd server
- function _Connect ()
- {
- if ( $this->_socket!==false )
- {
- // we are in persistent connection mode, so we have a socket
- // however, need to check whether it's still alive
- if ( !@feof ( $this->_socket ) )
- return $this->_socket;
-
- // force reopen
- $this->_socket = false;
- }
-
- $errno = 0;
- $errstr = "";
- $this->_connerror = false;
-
- if ( $this->_path )
- {
- $host = $this->_path;
- $port = 0;
- }
- else
- {
- $host = $this->_host;
- $port = $this->_port;
- }
-
- if ( $this->_timeout<=0 )
- $fp = @fsockopen ( $host, $port, $errno, $errstr );
- else
- $fp = @fsockopen ( $host, $port, $errno, $errstr, $this->_timeout );
-
- if ( !$fp )
- {
- if ( $this->_path )
- $location = $this->_path;
- else
- $location = "{$this->_host}:{$this->_port}";
-
- $errstr = trim ( $errstr );
- $this->_error = "connection to $location failed (errno=$errno, msg=$errstr)";
- $this->_connerror = true;
- return false;
- }
-
- // send my version
- // this is a subtle part. we must do it before (!) reading back from searchd.
- // because otherwise under some conditions (reported on FreeBSD for instance)
- // TCP stack could throttle write-write-read pattern because of Nagle.
- if ( !$this->_Send ( $fp, pack ( "N", 1 ), 4 ) )
- {
- fclose ( $fp );
- $this->_error = "failed to send client protocol version";
- return false;
- }
-
- // check version
- list(,$v) = unpack ( "N*", fread ( $fp, 4 ) );
- $v = (int)$v;
- if ( $v<1 )
- {
- fclose ( $fp );
- $this->_error = "expected searchd protocol version 1+, got version '$v'";
- return false;
- }
-
- return $fp;
- }
-
- /// get and check response packet from searchd server
- function _GetResponse ( $fp, $client_ver )
- {
- $response = "";
- $len = 0;
-
- $header = fread ( $fp, 8 );
- if ( strlen($header)==8 )
- {
- list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) );
- $left = $len;
- while ( $left>0 && !feof($fp) )
- {
- $chunk = fread ( $fp, min ( 8192, $left ) );
- if ( $chunk )
- {
- $response .= $chunk;
- $left -= strlen($chunk);
- }
- }
- }
- if ( $this->_socket === false )
- fclose ( $fp );
-
- // check response
- $read = strlen ( $response );
- if ( !$response || $read!=$len )
- {
- $this->_error = $len
- ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
- : "received zero-sized searchd response";
- return false;
- }
-
- // check status
- if ( $status==SEARCHD_WARNING )
- {
- list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) );
- $this->_warning = substr ( $response, 4, $wlen );
- return substr ( $response, 4+$wlen );
- }
- if ( $status==SEARCHD_ERROR )
- {
- $this->_error = "searchd error: " . substr ( $response, 4 );
- return false;
- }
- if ( $status==SEARCHD_RETRY )
- {
- $this->_error = "temporary searchd error: " . substr ( $response, 4 );
- return false;
- }
- if ( $status!=SEARCHD_OK )
- {
- $this->_error = "unknown status code '$status'";
- return false;
- }
-
- // check version
- if ( $ver<$client_ver )
- {
- $this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work",
- $ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff );
- }
-
- return $response;
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // searching
- /////////////////////////////////////////////////////////////////////////////
-
- /// set offset and count into result set,
- /// and optionally set max-matches and cutoff limits
- function SetLimits ( $offset, $limit, $max=0, $cutoff=0 )
- {
- assert ( is_int($offset) );
- assert ( is_int($limit) );
- assert ( $offset>=0 );
- assert ( $limit>0 );
- assert ( $max>=0 );
- $this->_offset = $offset;
- $this->_limit = $limit;
- if ( $max>0 )
- $this->_maxmatches = $max;
- if ( $cutoff>0 )
- $this->_cutoff = $cutoff;
- }
-
- /// set maximum query time, in milliseconds, per-index
- /// integer, 0 means "do not limit"
- function SetMaxQueryTime ( $max )
- {
- assert ( is_int($max) );
- assert ( $max>=0 );
- $this->_maxquerytime = $max;
- }
-
- /// set matching mode
- function SetMatchMode ( $mode )
- {
- assert ( $mode==SPH_MATCH_ALL
- || $mode==SPH_MATCH_ANY
- || $mode==SPH_MATCH_PHRASE
- || $mode==SPH_MATCH_BOOLEAN
- || $mode==SPH_MATCH_EXTENDED
- || $mode==SPH_MATCH_FULLSCAN
- || $mode==SPH_MATCH_EXTENDED2 );
- $this->_mode = $mode;
- }
-
- /// set ranking mode
- function SetRankingMode ( $ranker, $rankexpr="" )
- {
- assert ( $ranker>=0 && $ranker<SPH_RANK_TOTAL );
- assert ( is_string($rankexpr) );
- $this->_ranker = $ranker;
- $this->_rankexpr = $rankexpr;
- }
-
- /// set matches sorting mode
- function SetSortMode ( $mode, $sortby="" )
- {
- assert (
- $mode==SPH_SORT_RELEVANCE ||
- $mode==SPH_SORT_ATTR_DESC ||
- $mode==SPH_SORT_ATTR_ASC ||
- $mode==SPH_SORT_TIME_SEGMENTS ||
- $mode==SPH_SORT_EXTENDED ||
- $mode==SPH_SORT_EXPR );
- assert ( is_string($sortby) );
- assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 );
-
- $this->_sort = $mode;
- $this->_sortby = $sortby;
- }
-
- /// bind per-field weights by order
- /// DEPRECATED; use SetFieldWeights() instead
- function SetWeights ( $weights )
- {
- assert ( is_array($weights) );
- foreach ( $weights as $weight )
- assert ( is_int($weight) );
-
- $this->_weights = $weights;
- }
-
- /// bind per-field weights by name
- function SetFieldWeights ( $weights )
- {
- assert ( is_array($weights) );
- foreach ( $weights as $name=>$weight )
- {
- assert ( is_string($name) );
- assert ( is_int($weight) );
- }
- $this->_fieldweights = $weights;
- }
-
- /// bind per-index weights by name
- function SetIndexWeights ( $weights )
- {
- assert ( is_array($weights) );
- foreach ( $weights as $index=>$weight )
- {
- assert ( is_string($index) );
- assert ( is_int($weight) );
- }
- $this->_indexweights = $weights;
- }
-
- /// set IDs range to match
- /// only match records if document ID is beetwen $min and $max (inclusive)
- function SetIDRange ( $min, $max )
- {
- assert ( is_numeric($min) );
- assert ( is_numeric($max) );
- assert ( $min<=$max );
- $this->_min_id = $min;
- $this->_max_id = $max;
- }
-
- /// set values set filter
- /// only match records where $attribute value is in given set
- function SetFilter ( $attribute, $values, $exclude=false )
- {
- assert ( is_string($attribute) );
- assert ( is_array($values) );
- assert ( count($values) );
-
- if ( is_array($values) && count($values) )
- {
- foreach ( $values as $value )
- assert ( is_numeric($value) );
-
- $this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values );
- }
- }
-
- /// set range filter
- /// only match records if $attribute value is beetwen $min and $max (inclusive)
- function SetFilterRange ( $attribute, $min, $max, $exclude=false )
- {
- assert ( is_string($attribute) );
- assert ( is_numeric($min) );
- assert ( is_numeric($max) );
- assert ( $min<=$max );
-
- $this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
- }
-
- /// set float range filter
- /// only match records if $attribute value is beetwen $min and $max (inclusive)
- function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false )
- {
- assert ( is_string($attribute) );
- assert ( is_float($min) );
- assert ( is_float($max) );
- assert ( $min<=$max );
-
- $this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
- }
-
- /// setup anchor point for geosphere distance calculations
- /// required to use @geodist in filters and sorting
- /// latitude and longitude must be in radians
- function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long )
- {
- assert ( is_string($attrlat) );
- assert ( is_string($attrlong) );
- assert ( is_float($lat) );
- assert ( is_float($long) );
-
- $this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long );
- }
-
- /// set grouping attribute and function
- function SetGroupBy ( $attribute, $func, $groupsort="@group desc" )
- {
- assert ( is_string($attribute) );
- assert ( is_string($groupsort) );
- assert ( $func==SPH_GROUPBY_DAY
- || $func==SPH_GROUPBY_WEEK
- || $func==SPH_GROUPBY_MONTH
- || $func==SPH_GROUPBY_YEAR
- || $func==SPH_GROUPBY_ATTR
- || $func==SPH_GROUPBY_ATTRPAIR );
-
- $this->_groupby = $attribute;
- $this->_groupfunc = $func;
- $this->_groupsort = $groupsort;
- }
-
- /// set count-distinct attribute for group-by queries
- function SetGroupDistinct ( $attribute )
- {
- assert ( is_string($attribute) );
- $this->_groupdistinct = $attribute;
- }
-
- /// set distributed retries count and delay
- function SetRetries ( $count, $delay=0 )
- {
- assert ( is_int($count) && $count>=0 );
- assert ( is_int($delay) && $delay>=0 );
- $this->_retrycount = $count;
- $this->_retrydelay = $delay;
- }
-
- /// set result set format (hash or array; hash by default)
- /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
- function SetArrayResult ( $arrayresult )
- {
- assert ( is_bool($arrayresult) );
- $this->_arrayresult = $arrayresult;
- }
-
- /// set attribute values override
- /// there can be only one override per attribute
- /// $values must be a hash that maps document IDs to attribute values
- function SetOverride ( $attrname, $attrtype, $values )
- {
- assert ( is_string ( $attrname ) );
- assert ( in_array ( $attrtype, array ( SPH_ATTR_INTEGER, SPH_ATTR_TIMESTAMP, SPH_ATTR_BOOL, SPH_ATTR_FLOAT, SPH_ATTR_BIGINT ) ) );
- assert ( is_array ( $values ) );
-
- $this->_overrides[$attrname] = array ( "attr"=>$attrname, "type"=>$attrtype, "values"=>$values );
- }
-
- /// set select-list (attributes or expressions), SQL-like syntax
- function SetSelect ( $select )
- {
- assert ( is_string ( $select ) );
- $this->_select = $select;
- }
-
- //////////////////////////////////////////////////////////////////////////////
-
- /// clear all filters (for multi-queries)
- function ResetFilters ()
- {
- $this->_filters = array();
- $this->_anchor = array();
- }
-
- /// clear groupby settings (for multi-queries)
- function ResetGroupBy ()
- {
- $this->_groupby = "";
- $this->_groupfunc = SPH_GROUPBY_DAY;
- $this->_groupsort = "@group desc";
- $this->_groupdistinct= "";
- }
-
- /// clear all attribute value overrides (for multi-queries)
- function ResetOverrides ()
- {
- $this->_overrides = array ();
- }
-
- //////////////////////////////////////////////////////////////////////////////
-
- /// connect to searchd server, run given search query through given indexes,
- /// and return the search results
- function Query ( $query, $index="*", $comment="" )
- {
- assert ( empty($this->_reqs) );
-
- $this->AddQuery ( $query, $index, $comment );
- $results = $this->RunQueries ();
- $this->_reqs = array (); // just in case it failed too early
-
- if ( !is_array($results) )
- return false; // probably network error; error message should be already filled
-
- $this->_error = $results[0]["error"];
- $this->_warning = $results[0]["warning"];
- if ( $results[0]["status"]==SEARCHD_ERROR )
- return false;
- else
- return $results[0];
- }
-
- /// helper to pack floats in network byte order
- function _PackFloat ( $f )
- {
- $t1 = pack ( "f", $f ); // machine order
- list(,$t2) = unpack ( "L*", $t1 ); // int in machine order
- return pack ( "N", $t2 );
- }
-
- /// add query to multi-query batch
- /// returns index into results array from RunQueries() call
- function AddQuery ( $query, $index="*", $comment="" )
- {
- // mbstring workaround
- $this->_MBPush ();
-
- // build request
- $req = pack ( "NNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker );
- if ( $this->_ranker==SPH_RANK_EXPR )
- $req .= pack ( "N", strlen($this->_rankexpr) ) . $this->_rankexpr;
- $req .= pack ( "N", $this->_sort ); // (deprecated) sort mode
- $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby;
- $req .= pack ( "N", strlen($query) ) . $query; // query itself
- $req .= pack ( "N", count($this->_weights) ); // weights
- foreach ( $this->_weights as $weight )
- $req .= pack ( "N", (int)$weight );
- $req .= pack ( "N", strlen($index) ) . $index; // indexes
- $req .= pack ( "N", 1 ); // id64 range marker
- $req .= sphPackU64 ( $this->_min_id ) . sphPackU64 ( $this->_max_id ); // id64 range
-
- // filters
- $req .= pack ( "N", count($this->_filters) );
- foreach ( $this->_filters as $filter )
- {
- $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"];
- $req .= pack ( "N", $filter["type"] );
- switch ( $filter["type"] )
- {
- case SPH_FILTER_VALUES:
- $req .= pack ( "N", count($filter["values"]) );
- foreach ( $filter["values"] as $value )
- $req .= sphPackI64 ( $value );
- break;
-
- case SPH_FILTER_RANGE:
- $req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] );
- break;
-
- case SPH_FILTER_FLOATRANGE:
- $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
- break;
-
- default:
- assert ( 0 && "internal error: unhandled filter type" );
- }
- $req .= pack ( "N", $filter["exclude"] );
- }
-
- // group-by clause, max-matches count, group-sort clause, cutoff count
- $req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby;
- $req .= pack ( "N", $this->_maxmatches );
- $req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort;
- $req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay );
- $req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct;
-
- // anchor point
- if ( empty($this->_anchor) )
- {
- $req .= pack ( "N", 0 );
- } else
- {
- $a =& $this->_anchor;
- $req .= pack ( "N", 1 );
- $req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"];
- $req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"];
- $req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] );
- }
-
- // per-index weights
- $req .= pack ( "N", count($this->_indexweights) );
- foreach ( $this->_indexweights as $idx=>$weight )
- $req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight );
-
- // max query time
- $req .= pack ( "N", $this->_maxquerytime );
-
- // per-field weights
- $req .= pack ( "N", count($this->_fieldweights) );
- foreach ( $this->_fieldweights as $field=>$weight )
- $req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight );
-
- // comment
- $req .= pack ( "N", strlen($comment) ) . $comment;
-
- // attribute overrides
- $req .= pack ( "N", count($this->_overrides) );
- foreach ( $this->_overrides as $key => $entry )
- {
- $req .= pack ( "N", strlen($entry["attr"]) ) . $entry["attr"];
- $req .= pack ( "NN", $entry["type"], count($entry["values"]) );
- foreach ( $entry["values"] as $id=>$val )
- {
- assert ( is_numeric($id) );
- assert ( is_numeric($val) );
-
- $req .= sphPackU64 ( $id );
- switch ( $entry["type"] )
- {
- case SPH_ATTR_FLOAT: $req .= $this->_PackFloat ( $val ); break;
- case SPH_ATTR_BIGINT: $req .= sphPackI64 ( $val ); break;
- default: $req .= pack ( "N", $val ); break;
- }
- }
- }
-
- // select-list
- $req .= pack ( "N", strlen($this->_select) ) . $this->_select;
-
- // mbstring workaround
- $this->_MBPop ();
-
- // store request to requests array
- $this->_reqs[] = $req;
- return count($this->_reqs)-1;
- }
-
- /// connect to searchd, run queries batch, and return an array of result sets
- function RunQueries ()
- {
- if ( empty($this->_reqs) )
- {
- $this->_error = "no queries defined, issue AddQuery() first";
- return false;
- }
-
- // mbstring workaround
- $this->_MBPush ();
-
- if (!( $fp = $this->_Connect() ))
- {
- $this->_MBPop ();
- return false;
- }
-
- // send query, get response
- $nreqs = count($this->_reqs);
- $req = join ( "", $this->_reqs );
- $len = 8+strlen($req);
- $req = pack ( "nnNNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs ) . $req; // add header
-
- if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
- !( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) )
- {
- $this->_MBPop ();
- return false;
- }
-
- // query sent ok; we can reset reqs now
- $this->_reqs = array ();
-
- // parse and return response
- return $this->_ParseSearchResponse ( $response, $nreqs );
- }
-
- /// parse and return search query (or queries) response
- function _ParseSearchResponse ( $response, $nreqs )
- {
- $p = 0; // current position
- $max = strlen($response); // max position for checks, to protect against broken responses
-
- $results = array ();
- for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ )
- {
- $results[] = array();
- $result =& $results[$ires];
-
- $result["error"] = "";
- $result["warning"] = "";
-
- // extract status
- list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $result["status"] = $status;
- if ( $status!=SEARCHD_OK )
- {
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $message = substr ( $response, $p, $len ); $p += $len;
-
- if ( $status==SEARCHD_WARNING )
- {
- $result["warning"] = $message;
- } else
- {
- $result["error"] = $message;
- continue;
- }
- }
-
- // read schema
- $fields = array ();
- $attrs = array ();
-
- list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- while ( $nfields-->0 && $p<$max )
- {
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $fields[] = substr ( $response, $p, $len ); $p += $len;
- }
- $result["fields"] = $fields;
-
- list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- while ( $nattrs-->0 && $p<$max )
- {
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $attr = substr ( $response, $p, $len ); $p += $len;
- list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $attrs[$attr] = $type;
- }
- $result["attrs"] = $attrs;
-
- // read match count
- list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
-
- // read matches
- $idx = -1;
- while ( $count-->0 && $p<$max )
- {
- // index into result array
- $idx++;
-
- // parse document id and weight
- if ( $id64 )
- {
- $doc = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
- list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- }
- else
- {
- list ( $doc, $weight ) = array_values ( unpack ( "N*N*",
- substr ( $response, $p, 8 ) ) );
- $p += 8;
- $doc = sphFixUint($doc);
- }
- $weight = sprintf ( "%u", $weight );
-
- // create match entry
- if ( $this->_arrayresult )
- $result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight );
- else
- $result["matches"][$doc]["weight"] = $weight;
-
- // parse and create attributes
- $attrvals = array ();
- foreach ( $attrs as $attr=>$type )
- {
- // handle 64bit ints
- if ( $type==SPH_ATTR_BIGINT )
- {
- $attrvals[$attr] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
- continue;
- }
-
- // handle floats
- if ( $type==SPH_ATTR_FLOAT )
- {
- list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- list(,$fval) = unpack ( "f*", pack ( "L", $uval ) );
- $attrvals[$attr] = $fval;
- continue;
- }
-
- // handle everything else as unsigned ints
- list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- if ( $type==SPH_ATTR_MULTI )
- {
- $attrvals[$attr] = array ();
- $nvalues = $val;
- while ( $nvalues-->0 && $p<$max )
- {
- list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $attrvals[$attr][] = sphFixUint($val);
- }
- } else if ( $type==SPH_ATTR_MULTI64 )
- {
- $attrvals[$attr] = array ();
- $nvalues = $val;
- while ( $nvalues>0 && $p<$max )
- {
- $attrvals[$attr][] = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
- $nvalues -= 2;
- }
- } else if ( $type==SPH_ATTR_STRING )
- {
- $attrvals[$attr] = substr ( $response, $p, $val );
- $p += $val;
- } else
- {
- $attrvals[$attr] = sphFixUint($val);
- }
- }
-
- if ( $this->_arrayresult )
- $result["matches"][$idx]["attrs"] = $attrvals;
- else
- $result["matches"][$doc]["attrs"] = $attrvals;
- }
-
- list ( $total, $total_found, $msecs, $words ) =
- array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) );
- $result["total"] = sprintf ( "%u", $total );
- $result["total_found"] = sprintf ( "%u", $total_found );
- $result["time"] = sprintf ( "%.3f", $msecs/1000 );
- $p += 16;
-
- while ( $words-->0 && $p<$max )
- {
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $word = substr ( $response, $p, $len ); $p += $len;
- list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
- $result["words"][$word] = array (
- "docs"=>sprintf ( "%u", $docs ),
- "hits"=>sprintf ( "%u", $hits ) );
- }
- }
-
- $this->_MBPop ();
- return $results;
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // excerpts generation
- /////////////////////////////////////////////////////////////////////////////
-
- /// connect to searchd server, and generate exceprts (snippets)
- /// of given documents for given query. returns false on failure,
- /// an array of snippets on success
- function BuildExcerpts ( $docs, $index, $words, $opts=array() )
- {
- assert ( is_array($docs) );
- assert ( is_string($index) );
- assert ( is_string($words) );
- assert ( is_array($opts) );
-
- $this->_MBPush ();
-
- if (!( $fp = $this->_Connect() ))
- {
- $this->_MBPop();
- return false;
- }
-
- /////////////////
- // fixup options
- /////////////////
-
- if ( !isset($opts["before_match"]) ) $opts["before_match"] = "<b>";
- if ( !isset($opts["after_match"]) ) $opts["after_match"] = "</b>";
- if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... ";
- if ( !isset($opts["limit"]) ) $opts["limit"] = 256;
- if ( !isset($opts["limit_passages"]) ) $opts["limit_passages"] = 0;
- if ( !isset($opts["limit_words"]) ) $opts["limit_words"] = 0;
- if ( !isset($opts["around"]) ) $opts["around"] = 5;
- if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false;
- if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false;
- if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false;
- if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false;
- if ( !isset($opts["query_mode"]) ) $opts["query_mode"] = false;
- if ( !isset($opts["force_all_words"]) ) $opts["force_all_words"] = false;
- if ( !isset($opts["start_passage_id"]) ) $opts["start_passage_id"] = 1;
- if ( !isset($opts["load_files"]) ) $opts["load_files"] = false;
- if ( !isset($opts["html_strip_mode"]) ) $opts["html_strip_mode"] = "index";
- if ( !isset($opts["allow_empty"]) ) $opts["allow_empty"] = false;
- if ( !isset($opts["passage_boundary"]) ) $opts["passage_boundary"] = "none";
- if ( !isset($opts["emit_zones"]) ) $opts["emit_zones"] = false;
- if ( !isset($opts["load_files_scattered"]) ) $opts["load_files_scattered"] = false;
-
-
- /////////////////
- // build request
- /////////////////
-
- // v.1.2 req
- $flags = 1; // remove spaces
- if ( $opts["exact_phrase"] ) $flags |= 2;
- if ( $opts["single_passage"] ) $flags |= 4;
- if ( $opts["use_boundaries"] ) $flags |= 8;
- if ( $opts["weight_order"] ) $flags |= 16;
- if ( $opts["query_mode"] ) $flags |= 32;
- if ( $opts["force_all_words"] ) $flags |= 64;
- if ( $opts["load_files"] ) $flags |= 128;
- if ( $opts["allow_empty"] ) $flags |= 256;
- if ( $opts["emit_zones"] ) $flags |= 512;
- if ( $opts["load_files_scattered"] ) $flags |= 1024;
- $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags
- $req .= pack ( "N", strlen($index) ) . $index; // req index
- $req .= pack ( "N", strlen($words) ) . $words; // req words
-
- // options
- $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"];
- $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"];
- $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"];
- $req .= pack ( "NN", (int)$opts["limit"], (int)$opts["around"] );
- $req .= pack ( "NNN", (int)$opts["limit_passages"], (int)$opts["limit_words"], (int)$opts["start_passage_id"] ); // v.1.2
- $req .= pack ( "N", strlen($opts["html_strip_mode"]) ) . $opts["html_strip_mode"];
- $req .= pack ( "N", strlen($opts["passage_boundary"]) ) . $opts["passage_boundary"];
-
- // documents
- $req .= pack ( "N", count($docs) );
- foreach ( $docs as $doc )
- {
- assert ( is_string($doc) );
- $req .= pack ( "N", strlen($doc) ) . $doc;
- }
-
- ////////////////////////////
- // send query, get response
- ////////////////////////////
-
- $len = strlen($req);
- $req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header
- if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
- !( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ) )
- {
- $this->_MBPop ();
- return false;
- }
-
- //////////////////
- // parse response
- //////////////////
-
- $pos = 0;
- $res = array ();
- $rlen = strlen($response);
- for ( $i=0; $i<count($docs); $i++ )
- {
- list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) );
- $pos += 4;
-
- if ( $pos+$len > $rlen )
- {
- $this->_error = "incomplete reply";
- $this->_MBPop ();
- return false;
- }
- $res[] = $len ? substr ( $response, $pos, $len ) : "";
- $pos += $len;
- }
-
- $this->_MBPop ();
- return $res;
- }
-
-
- /////////////////////////////////////////////////////////////////////////////
- // keyword generation
- /////////////////////////////////////////////////////////////////////////////
-
- /// connect to searchd server, and generate keyword list for a given query
- /// returns false on failure,
- /// an array of words on success
- function BuildKeywords ( $query, $index, $hits )
- {
- assert ( is_string($query) );
- assert ( is_string($index) );
- assert ( is_bool($hits) );
-
- $this->_MBPush ();
-
- if (!( $fp = $this->_Connect() ))
- {
- $this->_MBPop();
- return false;
- }
-
- /////////////////
- // build request
- /////////////////
-
- // v.1.0 req
- $req = pack ( "N", strlen($query) ) . $query; // req query
- $req .= pack ( "N", strlen($index) ) . $index; // req index
- $req .= pack ( "N", (int)$hits );
-
- ////////////////////////////
- // send query, get response
- ////////////////////////////
-
- $len = strlen($req);
- $req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header
- if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
- !( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ) )
- {
- $this->_MBPop ();
- return false;
- }
-
- //////////////////
- // parse response
- //////////////////
-
- $pos = 0;
- $res = array ();
- $rlen = strlen($response);
- list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) );
- $pos += 4;
- for ( $i=0; $i<$nwords; $i++ )
- {
- list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
- $tokenized = $len ? substr ( $response, $pos, $len ) : "";
- $pos += $len;
-
- list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
- $normalized = $len ? substr ( $response, $pos, $len ) : "";
- $pos += $len;
-
- $res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized );
-
- if ( $hits )
- {
- list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) );
- $pos += 8;
- $res [$i]["docs"] = $ndocs;
- $res [$i]["hits"] = $nhits;
- }
-
- if ( $pos > $rlen )
- {
- $this->_error = "incomplete reply";
- $this->_MBPop ();
- return false;
- }
- }
-
- $this->_MBPop ();
- return $res;
- }
-
- function EscapeString ( $string )
- {
- $from = array ( '\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=' );
- $to = array ( '\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=' );
-
- return str_replace ( $from, $to, $string );
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // attribute updates
- /////////////////////////////////////////////////////////////////////////////
-
- /// batch update given attributes in given rows in given indexes
- /// returns amount of updated documents (0 or more) on success, or -1 on failure
- function UpdateAttributes ( $index, $attrs, $values, $mva=false )
- {
- // verify everything
- assert ( is_string($index) );
- assert ( is_bool($mva) );
-
- assert ( is_array($attrs) );
- foreach ( $attrs as $attr )
- assert ( is_string($attr) );
-
- assert ( is_array($values) );
- foreach ( $values as $id=>$entry )
- {
- assert ( is_numeric($id) );
- assert ( is_array($entry) );
- assert ( count($entry)==count($attrs) );
- foreach ( $entry as $v )
- {
- if ( $mva )
- {
- assert ( is_array($v) );
- foreach ( $v as $vv )
- assert ( is_int($vv) );
- } else
- assert ( is_int($v) );
- }
- }
-
- // build request
- $this->_MBPush ();
- $req = pack ( "N", strlen($index) ) . $index;
-
- $req .= pack ( "N", count($attrs) );
- foreach ( $attrs as $attr )
- {
- $req .= pack ( "N", strlen($attr) ) . $attr;
- $req .= pack ( "N", $mva ? 1 : 0 );
- }
-
- $req .= pack ( "N", count($values) );
- foreach ( $values as $id=>$entry )
- {
- $req .= sphPackU64 ( $id );
- foreach ( $entry as $v )
- {
- $req .= pack ( "N", $mva ? count($v) : $v );
- if ( $mva )
- foreach ( $v as $vv )
- $req .= pack ( "N", $vv );
- }
- }
-
- // connect, send query, get response
- if (!( $fp = $this->_Connect() ))
- {
- $this->_MBPop ();
- return -1;
- }
-
- $len = strlen($req);
- $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header
- if ( !$this->_Send ( $fp, $req, $len+8 ) )
- {
- $this->_MBPop ();
- return -1;
- }
-
- if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) ))
- {
- $this->_MBPop ();
- return -1;
- }
-
- // parse response
- list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) );
- $this->_MBPop ();
- return $updated;
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // persistent connections
- /////////////////////////////////////////////////////////////////////////////
-
- function Open()
- {
- if ( $this->_socket !== false )
- {
- $this->_error = 'already connected';
- return false;
- }
- if ( !$fp = $this->_Connect() )
- return false;
-
- // command, command version = 0, body length = 4, body = 1
- $req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 );
- if ( !$this->_Send ( $fp, $req, 12 ) )
- return false;
-
- $this->_socket = $fp;
- return true;
- }
-
- function Close()
- {
- if ( $this->_socket === false )
- {
- $this->_error = 'not connected';
- return false;
- }
-
- fclose ( $this->_socket );
- $this->_socket = false;
-
- return true;
- }
-
- //////////////////////////////////////////////////////////////////////////
- // status
- //////////////////////////////////////////////////////////////////////////
-
- function Status ()
- {
- $this->_MBPush ();
- if (!( $fp = $this->_Connect() ))
- {
- $this->_MBPop();
- return false;
- }
-
- $req = pack ( "nnNN", SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1
- if ( !( $this->_Send ( $fp, $req, 12 ) ) ||
- !( $response = $this->_GetResponse ( $fp, VER_COMMAND_STATUS ) ) )
- {
- $this->_MBPop ();
- return false;
- }
-
- $res = substr ( $response, 4 ); // just ignore length, error handling, etc
- $p = 0;
- list ( $rows, $cols ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
-
- $res = array();
- for ( $i=0; $i<$rows; $i++ )
- for ( $j=0; $j<$cols; $j++ )
- {
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
- $res[$i][] = substr ( $response, $p, $len ); $p += $len;
- }
-
- $this->_MBPop ();
- return $res;
- }
-
- //////////////////////////////////////////////////////////////////////////
- // flush
- //////////////////////////////////////////////////////////////////////////
-
- function FlushAttributes ()
- {
- $this->_MBPush ();
- if (!( $fp = $this->_Connect() ))
- {
- $this->_MBPop();
- return -1;
- }
-
- $req = pack ( "nnN", SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0 ); // len=0
- if ( !( $this->_Send ( $fp, $req, 8 ) ) ||
- !( $response = $this->_GetResponse ( $fp, VER_COMMAND_FLUSHATTRS ) ) )
- {
- $this->_MBPop ();
- return -1;
- }
-
- $tag = -1;
- if ( strlen($response)==4 )
- list(,$tag) = unpack ( "N*", $response );
- else
- $this->_error = "unexpected response length";
-
- $this->_MBPop ();
- return $tag;
- }
-}
-
-//
-// $Id: sphinxapi.php 3087 2012-01-30 23:07:35Z shodan $
-//
+<?php
+
+//
+// $Id: sphinxapi.php 3087 2012-01-30 23:07:35Z shodan $
+//
+
+//
+// Copyright (c) 2001-2012, Andrew Aksyonoff
+// Copyright (c) 2008-2012, Sphinx Technologies Inc
+// All rights reserved
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License. You should have
+// received a copy of the GPL license along with this program; if you
+// did not, you can find it at http://www.gnu.org/
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// PHP version of Sphinx searchd client (PHP API)
+/////////////////////////////////////////////////////////////////////////////
+
+/// known searchd commands
+define ( "SEARCHD_COMMAND_SEARCH", 0 );
+define ( "SEARCHD_COMMAND_EXCERPT", 1 );
+define ( "SEARCHD_COMMAND_UPDATE", 2 );
+define ( "SEARCHD_COMMAND_KEYWORDS", 3 );
+define ( "SEARCHD_COMMAND_PERSIST", 4 );
+define ( "SEARCHD_COMMAND_STATUS", 5 );
+define ( "SEARCHD_COMMAND_FLUSHATTRS", 7 );
+
+/// current client-side command implementation versions
+define ( "VER_COMMAND_SEARCH", 0x119 );
+define ( "VER_COMMAND_EXCERPT", 0x104 );
+define ( "VER_COMMAND_UPDATE", 0x102 );
+define ( "VER_COMMAND_KEYWORDS", 0x100 );
+define ( "VER_COMMAND_STATUS", 0x100 );
+define ( "VER_COMMAND_QUERY", 0x100 );
+define ( "VER_COMMAND_FLUSHATTRS", 0x100 );
+
+/// known searchd status codes
+define ( "SEARCHD_OK", 0 );
+define ( "SEARCHD_ERROR", 1 );
+define ( "SEARCHD_RETRY", 2 );
+define ( "SEARCHD_WARNING", 3 );
+
+/// known match modes
+define ( "SPH_MATCH_ALL", 0 );
+define ( "SPH_MATCH_ANY", 1 );
+define ( "SPH_MATCH_PHRASE", 2 );
+define ( "SPH_MATCH_BOOLEAN", 3 );
+define ( "SPH_MATCH_EXTENDED", 4 );
+define ( "SPH_MATCH_FULLSCAN", 5 );
+define ( "SPH_MATCH_EXTENDED2", 6 ); // extended engine V2 (TEMPORARY, WILL BE REMOVED)
+
+/// known ranking modes (ext2 only)
+define ( "SPH_RANK_PROXIMITY_BM25", 0 ); ///< default mode, phrase proximity major factor and BM25 minor one
+define ( "SPH_RANK_BM25", 1 ); ///< statistical mode, BM25 ranking only (faster but worse quality)
+define ( "SPH_RANK_NONE", 2 ); ///< no ranking, all matches get a weight of 1
+define ( "SPH_RANK_WORDCOUNT", 3 ); ///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
+define ( "SPH_RANK_PROXIMITY", 4 );
+define ( "SPH_RANK_MATCHANY", 5 );
+define ( "SPH_RANK_FIELDMASK", 6 );
+define ( "SPH_RANK_SPH04", 7 );
+define ( "SPH_RANK_EXPR", 8 );
+define ( "SPH_RANK_TOTAL", 9 );
+
+/// known sort modes
+define ( "SPH_SORT_RELEVANCE", 0 );
+define ( "SPH_SORT_ATTR_DESC", 1 );
+define ( "SPH_SORT_ATTR_ASC", 2 );
+define ( "SPH_SORT_TIME_SEGMENTS", 3 );
+define ( "SPH_SORT_EXTENDED", 4 );
+define ( "SPH_SORT_EXPR", 5 );
+
+/// known filter types
+define ( "SPH_FILTER_VALUES", 0 );
+define ( "SPH_FILTER_RANGE", 1 );
+define ( "SPH_FILTER_FLOATRANGE", 2 );
+
+/// known attribute types
+define ( "SPH_ATTR_INTEGER", 1 );
+define ( "SPH_ATTR_TIMESTAMP", 2 );
+define ( "SPH_ATTR_ORDINAL", 3 );
+define ( "SPH_ATTR_BOOL", 4 );
+define ( "SPH_ATTR_FLOAT", 5 );
+define ( "SPH_ATTR_BIGINT", 6 );
+define ( "SPH_ATTR_STRING", 7 );
+define ( "SPH_ATTR_MULTI", 0x40000001 );
+define ( "SPH_ATTR_MULTI64", 0x40000002 );
+
+/// known grouping functions
+define ( "SPH_GROUPBY_DAY", 0 );
+define ( "SPH_GROUPBY_WEEK", 1 );
+define ( "SPH_GROUPBY_MONTH", 2 );
+define ( "SPH_GROUPBY_YEAR", 3 );
+define ( "SPH_GROUPBY_ATTR", 4 );
+define ( "SPH_GROUPBY_ATTRPAIR", 5 );
+
+// important properties of PHP's integers:
+// - always signed (one bit short of PHP_INT_SIZE)
+// - conversion from string to int is saturated
+// - float is double
+// - div converts arguments to floats
+// - mod converts arguments to ints
+
+// the packing code below works as follows:
+// - when we got an int, just pack it
+// if performance is a problem, this is the branch users should aim for
+//
+// - otherwise, we got a number in string form
+// this might be due to different reasons, but we assume that this is
+// because it didn't fit into PHP int
+//
+// - factor the string into high and low ints for packing
+// - if we have bcmath, then it is used
+// - if we don't, we have to do it manually (this is the fun part)
+//
+// - x64 branch does factoring using ints
+// - x32 (ab)uses floats, since we can't fit unsigned 32-bit number into an int
+//
+// unpacking routines are pretty much the same.
+// - return ints if we can
+// - otherwise format number into a string
+
+/// pack 64-bit signed
+function sphPackI64 ( $v )
+{
+ assert ( is_numeric($v) );
+
+ // x64
+ if ( PHP_INT_SIZE>=8 )
+ {
+ $v = (int)$v;
+ return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
+ }
+
+ // x32, int
+ if ( is_int($v) )
+ return pack ( "NN", $v < 0 ? -1 : 0, $v );
+
+ // x32, bcmath
+ if ( function_exists("bcmul") )
+ {
+ if ( bccomp ( $v, 0 ) == -1 )
+ $v = bcadd ( "18446744073709551616", $v );
+ $h = bcdiv ( $v, "4294967296", 0 );
+ $l = bcmod ( $v, "4294967296" );
+ return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
+ }
+
+ // x32, no-bcmath
+ $p = max(0, strlen($v) - 13);
+ $lo = abs((float)substr($v, $p));
+ $hi = abs((float)substr($v, 0, $p));
+
+ $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
+ $q = floor($m/4294967296.0);
+ $l = $m - ($q*4294967296.0);
+ $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
+
+ if ( $v<0 )
+ {
+ if ( $l==0 )
+ $h = 4294967296.0 - $h;
+ else
+ {
+ $h = 4294967295.0 - $h;
+ $l = 4294967296.0 - $l;
+ }
+ }
+ return pack ( "NN", $h, $l );
+}
+
+/// pack 64-bit unsigned
+function sphPackU64 ( $v )
+{
+ assert ( is_numeric($v) );
+
+ // x64
+ if ( PHP_INT_SIZE>=8 )
+ {
+ assert ( $v>=0 );
+
+ // x64, int
+ if ( is_int($v) )
+ return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
+
+ // x64, bcmath
+ if ( function_exists("bcmul") )
+ {
+ $h = bcdiv ( $v, 4294967296, 0 );
+ $l = bcmod ( $v, 4294967296 );
+ return pack ( "NN", $h, $l );
+ }
+
+ // x64, no-bcmath
+ $p = max ( 0, strlen($v) - 13 );
+ $lo = (int)substr ( $v, $p );
+ $hi = (int)substr ( $v, 0, $p );
+
+ $m = $lo + $hi*1316134912;
+ $l = $m % 4294967296;
+ $h = $hi*2328 + (int)($m/4294967296);
+
+ return pack ( "NN", $h, $l );
+ }
+
+ // x32, int
+ if ( is_int($v) )
+ return pack ( "NN", 0, $v );
+
+ // x32, bcmath
+ if ( function_exists("bcmul") )
+ {
+ $h = bcdiv ( $v, "4294967296", 0 );
+ $l = bcmod ( $v, "4294967296" );
+ return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
+ }
+
+ // x32, no-bcmath
+ $p = max(0, strlen($v) - 13);
+ $lo = (float)substr($v, $p);
+ $hi = (float)substr($v, 0, $p);
+
+ $m = $lo + $hi*1316134912.0;
+ $q = floor($m / 4294967296.0);
+ $l = $m - ($q * 4294967296.0);
+ $h = $hi*2328.0 + $q;
+
+ return pack ( "NN", $h, $l );
+}
+
+// unpack 64-bit unsigned
+function sphUnpackU64 ( $v )
+{
+ list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
+
+ if ( PHP_INT_SIZE>=8 )
+ {
+ if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
+ if ( $lo<0 ) $lo += (1<<32);
+
+ // x64, int
+ if ( $hi<=2147483647 )
+ return ($hi<<32) + $lo;
+
+ // x64, bcmath
+ if ( function_exists("bcmul") )
+ return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
+
+ // x64, no-bcmath
+ $C = 100000;
+ $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
+ $l = (($hi % $C) << 32) + ($lo % $C);
+ if ( $l>$C )
+ {
+ $h += (int)($l / $C);
+ $l = $l % $C;
+ }
+
+ if ( $h==0 )
+ return $l;
+ return sprintf ( "%d%05d", $h, $l );
+ }
+
+ // x32, int
+ if ( $hi==0 )
+ {
+ if ( $lo>0 )
+ return $lo;
+ return sprintf ( "%u", $lo );
+ }
+
+ $hi = sprintf ( "%u", $hi );
+ $lo = sprintf ( "%u", $lo );
+
+ // x32, bcmath
+ if ( function_exists("bcmul") )
+ return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
+
+ // x32, no-bcmath
+ $hi = (float)$hi;
+ $lo = (float)$lo;
+
+ $q = floor($hi/10000000.0);
+ $r = $hi - $q*10000000.0;
+ $m = $lo + $r*4967296.0;
+ $mq = floor($m/10000000.0);
+ $l = $m - $mq*10000000.0;
+ $h = $q*4294967296.0 + $r*429.0 + $mq;
+
+ $h = sprintf ( "%.0f", $h );
+ $l = sprintf ( "%07.0f", $l );
+ if ( $h=="0" )
+ return sprintf( "%.0f", (float)$l );
+ return $h . $l;
+}
+
+// unpack 64-bit signed
+function sphUnpackI64 ( $v )
+{
+ list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
+
+ // x64
+ if ( PHP_INT_SIZE>=8 )
+ {
+ if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
+ if ( $lo<0 ) $lo += (1<<32);
+
+ return ($hi<<32) + $lo;
+ }
+
+ // x32, int
+ if ( $hi==0 )
+ {
+ if ( $lo>0 )
+ return $lo;
+ return sprintf ( "%u", $lo );
+ }
+ // x32, int
+ elseif ( $hi==-1 )
+ {
+ if ( $lo<0 )
+ return $lo;
+ return sprintf ( "%.0f", $lo - 4294967296.0 );
+ }
+
+ $neg = "";
+ $c = 0;
+ if ( $hi<0 )
+ {
+ $hi = ~$hi;
+ $lo = ~$lo;
+ $c = 1;
+ $neg = "-";
+ }
+
+ $hi = sprintf ( "%u", $hi );
+ $lo = sprintf ( "%u", $lo );
+
+ // x32, bcmath
+ if ( function_exists("bcmul") )
+ return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c );
+
+ // x32, no-bcmath
+ $hi = (float)$hi;
+ $lo = (float)$lo;
+
+ $q = floor($hi/10000000.0);
+ $r = $hi - $q*10000000.0;
+ $m = $lo + $r*4967296.0;
+ $mq = floor($m/10000000.0);
+ $l = $m - $mq*10000000.0 + $c;
+ $h = $q*4294967296.0 + $r*429.0 + $mq;
+ if ( $l==10000000 )
+ {
+ $l = 0;
+ $h += 1;
+ }
+
+ $h = sprintf ( "%.0f", $h );
+ $l = sprintf ( "%07.0f", $l );
+ if ( $h=="0" )
+ return $neg . sprintf( "%.0f", (float)$l );
+ return $neg . $h . $l;
+}
+
+
+function sphFixUint ( $value )
+{
+ if ( PHP_INT_SIZE>=8 )
+ {
+ // x64 route, workaround broken unpack() in 5.2.2+
+ if ( $value<0 ) $value += (1<<32);
+ return $value;
+ }
+ else
+ {
+ // x32 route, workaround php signed/unsigned braindamage
+ return sprintf ( "%u", $value );
+ }
+}
+
+
+/// sphinx searchd client class
+class SphinxClient
+{
+ var $_host; ///< searchd host (default is "localhost")
+ var $_port; ///< searchd port (default is 9312)
+ var $_offset; ///< how many records to seek from result-set start (default is 0)
+ var $_limit; ///< how many records to return from result-set starting at offset (default is 20)
+ var $_mode; ///< query matching mode (default is SPH_MATCH_ALL)
+ var $_weights; ///< per-field weights (default is 1 for all fields)
+ var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
+ var $_sortby; ///< attribute to sort by (defualt is "")
+ var $_min_id; ///< min ID to match (default is 0, which means no limit)
+ var $_max_id; ///< max ID to match (default is 0, which means no limit)
+ var $_filters; ///< search filters
+ var $_groupby; ///< group-by attribute name
+ var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with)
+ var $_groupsort; ///< group-by sorting clause (to sort groups in result set with)
+ var $_groupdistinct;///< group-by count-distinct attribute
+ var $_maxmatches; ///< max matches to retrieve
+ var $_cutoff; ///< cutoff to stop searching at (default is 0)
+ var $_retrycount; ///< distributed retries count
+ var $_retrydelay; ///< distributed retries delay
+ var $_anchor; ///< geographical anchor point
+ var $_indexweights; ///< per-index weights
+ var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
+ var $_rankexpr; ///< ranking mode expression (for SPH_RANK_EXPR)
+ var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit)
+ var $_fieldweights; ///< per-field-name weights
+ var $_overrides; ///< per-query attribute values overrides
+ var $_select; ///< select-list (attributes or expressions, with optional aliases)
+
+ var $_error; ///< last error message
+ var $_warning; ///< last warning message
+ var $_connerror; ///< connection error vs remote error flag
+
+ var $_reqs; ///< requests array for multi-query
+ var $_mbenc; ///< stored mbstring encoding
+ var $_arrayresult; ///< whether $result["matches"] should be a hash or an array
+ var $_timeout; ///< connect timeout
+
+ /////////////////////////////////////////////////////////////////////////////
+ // common stuff
+ /////////////////////////////////////////////////////////////////////////////
+
+ /// create a new client object and fill defaults
+ function SphinxClient ()
+ {
+ // per-client-object settings
+ $this->_host = "localhost";
+ $this->_port = 9312;
+ $this->_path = false;
+ $this->_socket = false;
+
+ // per-query settings
+ $this->_offset = 0;
+ $this->_limit = 20;
+ $this->_mode = SPH_MATCH_ALL;
+ $this->_weights = array ();
+ $this->_sort = SPH_SORT_RELEVANCE;
+ $this->_sortby = "";
+ $this->_min_id = 0;
+ $this->_max_id = 0;
+ $this->_filters = array ();
+ $this->_groupby = "";
+ $this->_groupfunc = SPH_GROUPBY_DAY;
+ $this->_groupsort = "@group desc";
+ $this->_groupdistinct= "";
+ $this->_maxmatches = 1000;
+ $this->_cutoff = 0;
+ $this->_retrycount = 0;
+ $this->_retrydelay = 0;
+ $this->_anchor = array ();
+ $this->_indexweights= array ();
+ $this->_ranker = SPH_RANK_PROXIMITY_BM25;
+ $this->_rankexpr = "";
+ $this->_maxquerytime= 0;
+ $this->_fieldweights= array();
+ $this->_overrides = array();
+ $this->_select = "*";
+
+ $this->_error = ""; // per-reply fields (for single-query case)
+ $this->_warning = "";
+ $this->_connerror = false;
+
+ $this->_reqs = array (); // requests storage (for multi-query case)
+ $this->_mbenc = "";
+ $this->_arrayresult = false;
+ $this->_timeout = 0;
+ }
+
+ function __destruct()
+ {
+ if ( $this->_socket !== false )
+ fclose ( $this->_socket );
+ }
+
+ /// get last error message (string)
+ function GetLastError ()
+ {
+ return $this->_error;
+ }
+
+ /// get last warning message (string)
+ function GetLastWarning ()
+ {
+ return $this->_warning;
+ }
+
+ /// get last error flag (to tell network connection errors from searchd errors or broken responses)
+ function IsConnectError()
+ {
+ return $this->_connerror;
+ }
+
+ /// set searchd host name (string) and port (integer)
+ function SetServer ( $host, $port = 0 )
+ {
+ assert ( is_string($host) );
+ if ( $host[0] == '/')
+ {
+ $this->_path = 'unix://' . $host;
+ return;
+ }
+ if ( substr ( $host, 0, 7 )=="unix://" )
+ {
+ $this->_path = $host;
+ return;
+ }
+
+ assert ( is_int($port) );
+ $this->_host = $host;
+ $this->_port = $port;
+ $this->_path = '';
+
+ }
+
+ /// set server connection timeout (0 to remove)
+ function SetConnectTimeout ( $timeout )
+ {
+ assert ( is_numeric($timeout) );
+ $this->_timeout = $timeout;
+ }
+
+
+ function _Send ( $handle, $data, $length )
+ {
+ if ( feof($handle) || fwrite ( $handle, $data, $length ) !== $length )
+ {
+ $this->_error = 'connection unexpectedly closed (timed out?)';
+ $this->_connerror = true;
+ return false;
+ }
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ /// enter mbstring workaround mode
+ function _MBPush ()
+ {
+ $this->_mbenc = "";
+ if ( ini_get ( "mbstring.func_overload" ) & 2 )
+ {
+ $this->_mbenc = mb_internal_encoding();
+ mb_internal_encoding ( "latin1" );
+ }
+ }
+
+ /// leave mbstring workaround mode
+ function _MBPop ()
+ {
+ if ( $this->_mbenc )
+ mb_internal_encoding ( $this->_mbenc );
+ }
+
+ /// connect to searchd server
+ function _Connect ()
+ {
+ if ( $this->_socket!==false )
+ {
+ // we are in persistent connection mode, so we have a socket
+ // however, need to check whether it's still alive
+ if ( !@feof ( $this->_socket ) )
+ return $this->_socket;
+
+ // force reopen
+ $this->_socket = false;
+ }
+
+ $errno = 0;
+ $errstr = "";
+ $this->_connerror = false;
+
+ if ( $this->_path )
+ {
+ $host = $this->_path;
+ $port = 0;
+ }
+ else
+ {
+ $host = $this->_host;
+ $port = $this->_port;
+ }
+
+ if ( $this->_timeout<=0 )
+ $fp = @fsockopen ( $host, $port, $errno, $errstr );
+ else
+ $fp = @fsockopen ( $host, $port, $errno, $errstr, $this->_timeout );
+
+ if ( !$fp )
+ {
+ if ( $this->_path )
+ $location = $this->_path;
+ else
+ $location = "{$this->_host}:{$this->_port}";
+
+ $errstr = trim ( $errstr );
+ $this->_error = "connection to $location failed (errno=$errno, msg=$errstr)";
+ $this->_connerror = true;
+ return false;
+ }
+
+ // send my version
+ // this is a subtle part. we must do it before (!) reading back from searchd.
+ // because otherwise under some conditions (reported on FreeBSD for instance)
+ // TCP stack could throttle write-write-read pattern because of Nagle.
+ if ( !$this->_Send ( $fp, pack ( "N", 1 ), 4 ) )
+ {
+ fclose ( $fp );
+ $this->_error = "failed to send client protocol version";
+ return false;
+ }
+
+ // check version
+ list(,$v) = unpack ( "N*", fread ( $fp, 4 ) );
+ $v = (int)$v;
+ if ( $v<1 )
+ {
+ fclose ( $fp );
+ $this->_error = "expected searchd protocol version 1+, got version '$v'";
+ return false;
+ }
+
+ return $fp;
+ }
+
+ /// get and check response packet from searchd server
+ function _GetResponse ( $fp, $client_ver )
+ {
+ $response = "";
+ $len = 0;
+
+ $header = fread ( $fp, 8 );
+ if ( strlen($header)==8 )
+ {
+ list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) );
+ $left = $len;
+ while ( $left>0 && !feof($fp) )
+ {
+ $chunk = fread ( $fp, min ( 8192, $left ) );
+ if ( $chunk )
+ {
+ $response .= $chunk;
+ $left -= strlen($chunk);
+ }
+ }
+ }
+ if ( $this->_socket === false )
+ fclose ( $fp );
+
+ // check response
+ $read = strlen ( $response );
+ if ( !$response || $read!=$len )
+ {
+ $this->_error = $len
+ ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
+ : "received zero-sized searchd response";
+ return false;
+ }
+
+ // check status
+ if ( $status==SEARCHD_WARNING )
+ {
+ list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) );
+ $this->_warning = substr ( $response, 4, $wlen );
+ return substr ( $response, 4+$wlen );
+ }
+ if ( $status==SEARCHD_ERROR )
+ {
+ $this->_error = "searchd error: " . substr ( $response, 4 );
+ return false;
+ }
+ if ( $status==SEARCHD_RETRY )
+ {
+ $this->_error = "temporary searchd error: " . substr ( $response, 4 );
+ return false;
+ }
+ if ( $status!=SEARCHD_OK )
+ {
+ $this->_error = "unknown status code '$status'";
+ return false;
+ }
+
+ // check version
+ if ( $ver<$client_ver )
+ {
+ $this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work",
+ $ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff );
+ }
+
+ return $response;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // searching
+ /////////////////////////////////////////////////////////////////////////////
+
+ /// set offset and count into result set,
+ /// and optionally set max-matches and cutoff limits
+ function SetLimits ( $offset, $limit, $max=0, $cutoff=0 )
+ {
+ assert ( is_int($offset) );
+ assert ( is_int($limit) );
+ assert ( $offset>=0 );
+ assert ( $limit>0 );
+ assert ( $max>=0 );
+ $this->_offset = $offset;
+ $this->_limit = $limit;
+ if ( $max>0 )
+ $this->_maxmatches = $max;
+ if ( $cutoff>0 )
+ $this->_cutoff = $cutoff;
+ }
+
+ /// set maximum query time, in milliseconds, per-index
+ /// integer, 0 means "do not limit"
+ function SetMaxQueryTime ( $max )
+ {
+ assert ( is_int($max) );
+ assert ( $max>=0 );
+ $this->_maxquerytime = $max;
+ }
+
+ /// set matching mode
+ function SetMatchMode ( $mode )
+ {
+ assert ( $mode==SPH_MATCH_ALL
+ || $mode==SPH_MATCH_ANY
+ || $mode==SPH_MATCH_PHRASE
+ || $mode==SPH_MATCH_BOOLEAN
+ || $mode==SPH_MATCH_EXTENDED
+ || $mode==SPH_MATCH_FULLSCAN
+ || $mode==SPH_MATCH_EXTENDED2 );
+ $this->_mode = $mode;
+ }
+
+ /// set ranking mode
+ function SetRankingMode ( $ranker, $rankexpr="" )
+ {
+ assert ( $ranker>=0 && $ranker<SPH_RANK_TOTAL );
+ assert ( is_string($rankexpr) );
+ $this->_ranker = $ranker;
+ $this->_rankexpr = $rankexpr;
+ }
+
+ /// set matches sorting mode
+ function SetSortMode ( $mode, $sortby="" )
+ {
+ assert (
+ $mode==SPH_SORT_RELEVANCE ||
+ $mode==SPH_SORT_ATTR_DESC ||
+ $mode==SPH_SORT_ATTR_ASC ||
+ $mode==SPH_SORT_TIME_SEGMENTS ||
+ $mode==SPH_SORT_EXTENDED ||
+ $mode==SPH_SORT_EXPR );
+ assert ( is_string($sortby) );
+ assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 );
+
+ $this->_sort = $mode;
+ $this->_sortby = $sortby;
+ }
+
+ /// bind per-field weights by order
+ /// DEPRECATED; use SetFieldWeights() instead
+ function SetWeights ( $weights )
+ {
+ assert ( is_array($weights) );
+ foreach ( $weights as $weight )
+ assert ( is_int($weight) );
+
+ $this->_weights = $weights;
+ }
+
+ /// bind per-field weights by name
+ function SetFieldWeights ( $weights )
+ {
+ assert ( is_array($weights) );
+ foreach ( $weights as $name=>$weight )
+ {
+ assert ( is_string($name) );
+ assert ( is_int($weight) );
+ }
+ $this->_fieldweights = $weights;
+ }
+
+ /// bind per-index weights by name
+ function SetIndexWeights ( $weights )
+ {
+ assert ( is_array($weights) );
+ foreach ( $weights as $index=>$weight )
+ {
+ assert ( is_string($index) );
+ assert ( is_int($weight) );
+ }
+ $this->_indexweights = $weights;
+ }
+
+ /// set IDs range to match
+ /// only match records if document ID is beetwen $min and $max (inclusive)
+ function SetIDRange ( $min, $max )
+ {
+ assert ( is_numeric($min) );
+ assert ( is_numeric($max) );
+ assert ( $min<=$max );
+ $this->_min_id = $min;
+ $this->_max_id = $max;
+ }
+
+ /// set values set filter
+ /// only match records where $attribute value is in given set
+ function SetFilter ( $attribute, $values, $exclude=false )
+ {
+ assert ( is_string($attribute) );
+ assert ( is_array($values) );
+ assert ( count($values) );
+
+ if ( is_array($values) && count($values) )
+ {
+ foreach ( $values as $value )
+ assert ( is_numeric($value) );
+
+ $this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values );
+ }
+ }
+
+ /// set range filter
+ /// only match records if $attribute value is beetwen $min and $max (inclusive)
+ function SetFilterRange ( $attribute, $min, $max, $exclude=false )
+ {
+ assert ( is_string($attribute) );
+ assert ( is_numeric($min) );
+ assert ( is_numeric($max) );
+ assert ( $min<=$max );
+
+ $this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
+ }
+
+ /// set float range filter
+ /// only match records if $attribute value is beetwen $min and $max (inclusive)
+ function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false )
+ {
+ assert ( is_string($attribute) );
+ assert ( is_float($min) );
+ assert ( is_float($max) );
+ assert ( $min<=$max );
+
+ $this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
+ }
+
+ /// setup anchor point for geosphere distance calculations
+ /// required to use @geodist in filters and sorting
+ /// latitude and longitude must be in radians
+ function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long )
+ {
+ assert ( is_string($attrlat) );
+ assert ( is_string($attrlong) );
+ assert ( is_float($lat) );
+ assert ( is_float($long) );
+
+ $this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long );
+ }
+
+ /// set grouping attribute and function
+ function SetGroupBy ( $attribute, $func, $groupsort="@group desc" )
+ {
+ assert ( is_string($attribute) );
+ assert ( is_string($groupsort) );
+ assert ( $func==SPH_GROUPBY_DAY
+ || $func==SPH_GROUPBY_WEEK
+ || $func==SPH_GROUPBY_MONTH
+ || $func==SPH_GROUPBY_YEAR
+ || $func==SPH_GROUPBY_ATTR
+ || $func==SPH_GROUPBY_ATTRPAIR );
+
+ $this->_groupby = $attribute;
+ $this->_groupfunc = $func;
+ $this->_groupsort = $groupsort;
+ }
+
+ /// set count-distinct attribute for group-by queries
+ function SetGroupDistinct ( $attribute )
+ {
+ assert ( is_string($attribute) );
+ $this->_groupdistinct = $attribute;
+ }
+
+ /// set distributed retries count and delay
+ function SetRetries ( $count, $delay=0 )
+ {
+ assert ( is_int($count) && $count>=0 );
+ assert ( is_int($delay) && $delay>=0 );
+ $this->_retrycount = $count;
+ $this->_retrydelay = $delay;
+ }
+
+ /// set result set format (hash or array; hash by default)
+ /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
+ function SetArrayResult ( $arrayresult )
+ {
+ assert ( is_bool($arrayresult) );
+ $this->_arrayresult = $arrayresult;
+ }
+
+ /// set attribute values override
+ /// there can be only one override per attribute
+ /// $values must be a hash that maps document IDs to attribute values
+ function SetOverride ( $attrname, $attrtype, $values )
+ {
+ assert ( is_string ( $attrname ) );
+ assert ( in_array ( $attrtype, array ( SPH_ATTR_INTEGER, SPH_ATTR_TIMESTAMP, SPH_ATTR_BOOL, SPH_ATTR_FLOAT, SPH_ATTR_BIGINT ) ) );
+ assert ( is_array ( $values ) );
+
+ $this->_overrides[$attrname] = array ( "attr"=>$attrname, "type"=>$attrtype, "values"=>$values );
+ }
+
+ /// set select-list (attributes or expressions), SQL-like syntax
+ function SetSelect ( $select )
+ {
+ assert ( is_string ( $select ) );
+ $this->_select = $select;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ /// clear all filters (for multi-queries)
+ function ResetFilters ()
+ {
+ $this->_filters = array();
+ $this->_anchor = array();
+ }
+
+ /// clear groupby settings (for multi-queries)
+ function ResetGroupBy ()
+ {
+ $this->_groupby = "";
+ $this->_groupfunc = SPH_GROUPBY_DAY;
+ $this->_groupsort = "@group desc";
+ $this->_groupdistinct= "";
+ }
+
+ /// clear all attribute value overrides (for multi-queries)
+ function ResetOverrides ()
+ {
+ $this->_overrides = array ();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ /// connect to searchd server, run given search query through given indexes,
+ /// and return the search results
+ function Query ( $query, $index="*", $comment="" )
+ {
+ assert ( empty($this->_reqs) );
+
+ $this->AddQuery ( $query, $index, $comment );
+ $results = $this->RunQueries ();
+ $this->_reqs = array (); // just in case it failed too early
+
+ if ( !is_array($results) )
+ return false; // probably network error; error message should be already filled
+
+ $this->_error = $results[0]["error"];
+ $this->_warning = $results[0]["warning"];
+ if ( $results[0]["status"]==SEARCHD_ERROR )
+ return false;
+ else
+ return $results[0];
+ }
+
+ /// helper to pack floats in network byte order
+ function _PackFloat ( $f )
+ {
+ $t1 = pack ( "f", $f ); // machine order
+ list(,$t2) = unpack ( "L*", $t1 ); // int in machine order
+ return pack ( "N", $t2 );
+ }
+
+ /// add query to multi-query batch
+ /// returns index into results array from RunQueries() call
+ function AddQuery ( $query, $index="*", $comment="" )
+ {
+ // mbstring workaround
+ $this->_MBPush ();
+
+ // build request
+ $req = pack ( "NNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker );
+ if ( $this->_ranker==SPH_RANK_EXPR )
+ $req .= pack ( "N", strlen($this->_rankexpr) ) . $this->_rankexpr;
+ $req .= pack ( "N", $this->_sort ); // (deprecated) sort mode
+ $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby;
+ $req .= pack ( "N", strlen($query) ) . $query; // query itself
+ $req .= pack ( "N", count($this->_weights) ); // weights
+ foreach ( $this->_weights as $weight )
+ $req .= pack ( "N", (int)$weight );
+ $req .= pack ( "N", strlen($index) ) . $index; // indexes
+ $req .= pack ( "N", 1 ); // id64 range marker
+ $req .= sphPackU64 ( $this->_min_id ) . sphPackU64 ( $this->_max_id ); // id64 range
+
+ // filters
+ $req .= pack ( "N", count($this->_filters) );
+ foreach ( $this->_filters as $filter )
+ {
+ $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"];
+ $req .= pack ( "N", $filter["type"] );
+ switch ( $filter["type"] )
+ {
+ case SPH_FILTER_VALUES:
+ $req .= pack ( "N", count($filter["values"]) );
+ foreach ( $filter["values"] as $value )
+ $req .= sphPackI64 ( $value );
+ break;
+
+ case SPH_FILTER_RANGE:
+ $req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] );
+ break;
+
+ case SPH_FILTER_FLOATRANGE:
+ $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
+ break;
+
+ default:
+ assert ( 0 && "internal error: unhandled filter type" );
+ }
+ $req .= pack ( "N", $filter["exclude"] );
+ }
+
+ // group-by clause, max-matches count, group-sort clause, cutoff count
+ $req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby;
+ $req .= pack ( "N", $this->_maxmatches );
+ $req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort;
+ $req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay );
+ $req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct;
+
+ // anchor point
+ if ( empty($this->_anchor) )
+ {
+ $req .= pack ( "N", 0 );
+ } else
+ {
+ $a =& $this->_anchor;
+ $req .= pack ( "N", 1 );
+ $req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"];
+ $req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"];
+ $req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] );
+ }
+
+ // per-index weights
+ $req .= pack ( "N", count($this->_indexweights) );
+ foreach ( $this->_indexweights as $idx=>$weight )
+ $req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight );
+
+ // max query time
+ $req .= pack ( "N", $this->_maxquerytime );
+
+ // per-field weights
+ $req .= pack ( "N", count($this->_fieldweights) );
+ foreach ( $this->_fieldweights as $field=>$weight )
+ $req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight );
+
+ // comment
+ $req .= pack ( "N", strlen($comment) ) . $comment;
+
+ // attribute overrides
+ $req .= pack ( "N", count($this->_overrides) );
+ foreach ( $this->_overrides as $key => $entry )
+ {
+ $req .= pack ( "N", strlen($entry["attr"]) ) . $entry["attr"];
+ $req .= pack ( "NN", $entry["type"], count($entry["values"]) );
+ foreach ( $entry["values"] as $id=>$val )
+ {
+ assert ( is_numeric($id) );
+ assert ( is_numeric($val) );
+
+ $req .= sphPackU64 ( $id );
+ switch ( $entry["type"] )
+ {
+ case SPH_ATTR_FLOAT: $req .= $this->_PackFloat ( $val ); break;
+ case SPH_ATTR_BIGINT: $req .= sphPackI64 ( $val ); break;
+ default: $req .= pack ( "N", $val ); break;
+ }
+ }
+ }
+
+ // select-list
+ $req .= pack ( "N", strlen($this->_select) ) . $this->_select;
+
+ // mbstring workaround
+ $this->_MBPop ();
+
+ // store request to requests array
+ $this->_reqs[] = $req;
+ return count($this->_reqs)-1;
+ }
+
+ /// connect to searchd, run queries batch, and return an array of result sets
+ function RunQueries ()
+ {
+ if ( empty($this->_reqs) )
+ {
+ $this->_error = "no queries defined, issue AddQuery() first";
+ return false;
+ }
+
+ // mbstring workaround
+ $this->_MBPush ();
+
+ if (!( $fp = $this->_Connect() ))
+ {
+ $this->_MBPop ();
+ return false;
+ }
+
+ // send query, get response
+ $nreqs = count($this->_reqs);
+ $req = join ( "", $this->_reqs );
+ $len = 8+strlen($req);
+ $req = pack ( "nnNNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs ) . $req; // add header
+
+ if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
+ !( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) )
+ {
+ $this->_MBPop ();
+ return false;
+ }
+
+ // query sent ok; we can reset reqs now
+ $this->_reqs = array ();
+
+ // parse and return response
+ return $this->_ParseSearchResponse ( $response, $nreqs );
+ }
+
+ /// parse and return search query (or queries) response
+ function _ParseSearchResponse ( $response, $nreqs )
+ {
+ $p = 0; // current position
+ $max = strlen($response); // max position for checks, to protect against broken responses
+
+ $results = array ();
+ for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ )
+ {
+ $results[] = array();
+ $result =& $results[$ires];
+
+ $result["error"] = "";
+ $result["warning"] = "";
+
+ // extract status
+ list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $result["status"] = $status;
+ if ( $status!=SEARCHD_OK )
+ {
+ list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $message = substr ( $response, $p, $len ); $p += $len;
+
+ if ( $status==SEARCHD_WARNING )
+ {
+ $result["warning"] = $message;
+ } else
+ {
+ $result["error"] = $message;
+ continue;
+ }
+ }
+
+ // read schema
+ $fields = array ();
+ $attrs = array ();
+
+ list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ while ( $nfields-->0 && $p<$max )
+ {
+ list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $fields[] = substr ( $response, $p, $len ); $p += $len;
+ }
+ $result["fields"] = $fields;
+
+ list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ while ( $nattrs-->0 && $p<$max )
+ {
+ list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $attr = substr ( $response, $p, $len ); $p += $len;
+ list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $attrs[$attr] = $type;
+ }
+ $result["attrs"] = $attrs;
+
+ // read match count
+ list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+
+ // read matches
+ $idx = -1;
+ while ( $count-->0 && $p<$max )
+ {
+ // index into result array
+ $idx++;
+
+ // parse document id and weight
+ if ( $id64 )
+ {
+ $doc = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
+ list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ }
+ else
+ {
+ list ( $doc, $weight ) = array_values ( unpack ( "N*N*",
+ substr ( $response, $p, 8 ) ) );
+ $p += 8;
+ $doc = sphFixUint($doc);
+ }
+ $weight = sprintf ( "%u", $weight );
+
+ // create match entry
+ if ( $this->_arrayresult )
+ $result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight );
+ else
+ $result["matches"][$doc]["weight"] = $weight;
+
+ // parse and create attributes
+ $attrvals = array ();
+ foreach ( $attrs as $attr=>$type )
+ {
+ // handle 64bit ints
+ if ( $type==SPH_ATTR_BIGINT )
+ {
+ $attrvals[$attr] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
+ continue;
+ }
+
+ // handle floats
+ if ( $type==SPH_ATTR_FLOAT )
+ {
+ list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ list(,$fval) = unpack ( "f*", pack ( "L", $uval ) );
+ $attrvals[$attr] = $fval;
+ continue;
+ }
+
+ // handle everything else as unsigned ints
+ list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ if ( $type==SPH_ATTR_MULTI )
+ {
+ $attrvals[$attr] = array ();
+ $nvalues = $val;
+ while ( $nvalues-->0 && $p<$max )
+ {
+ list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $attrvals[$attr][] = sphFixUint($val);
+ }
+ } else if ( $type==SPH_ATTR_MULTI64 )
+ {
+ $attrvals[$attr] = array ();
+ $nvalues = $val;
+ while ( $nvalues>0 && $p<$max )
+ {
+ $attrvals[$attr][] = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
+ $nvalues -= 2;
+ }
+ } else if ( $type==SPH_ATTR_STRING )
+ {
+ $attrvals[$attr] = substr ( $response, $p, $val );
+ $p += $val;
+ } else
+ {
+ $attrvals[$attr] = sphFixUint($val);
+ }
+ }
+
+ if ( $this->_arrayresult )
+ $result["matches"][$idx]["attrs"] = $attrvals;
+ else
+ $result["matches"][$doc]["attrs"] = $attrvals;
+ }
+
+ list ( $total, $total_found, $msecs, $words ) =
+ array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) );
+ $result["total"] = sprintf ( "%u", $total );
+ $result["total_found"] = sprintf ( "%u", $total_found );
+ $result["time"] = sprintf ( "%.3f", $msecs/1000 );
+ $p += 16;
+
+ while ( $words-->0 && $p<$max )
+ {
+ list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $word = substr ( $response, $p, $len ); $p += $len;
+ list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
+ $result["words"][$word] = array (
+ "docs"=>sprintf ( "%u", $docs ),
+ "hits"=>sprintf ( "%u", $hits ) );
+ }
+ }
+
+ $this->_MBPop ();
+ return $results;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // excerpts generation
+ /////////////////////////////////////////////////////////////////////////////
+
+ /// connect to searchd server, and generate exceprts (snippets)
+ /// of given documents for given query. returns false on failure,
+ /// an array of snippets on success
+ function BuildExcerpts ( $docs, $index, $words, $opts=array() )
+ {
+ assert ( is_array($docs) );
+ assert ( is_string($index) );
+ assert ( is_string($words) );
+ assert ( is_array($opts) );
+
+ $this->_MBPush ();
+
+ if (!( $fp = $this->_Connect() ))
+ {
+ $this->_MBPop();
+ return false;
+ }
+
+ /////////////////
+ // fixup options
+ /////////////////
+
+ if ( !isset($opts["before_match"]) ) $opts["before_match"] = "<b>";
+ if ( !isset($opts["after_match"]) ) $opts["after_match"] = "</b>";
+ if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... ";
+ if ( !isset($opts["limit"]) ) $opts["limit"] = 256;
+ if ( !isset($opts["limit_passages"]) ) $opts["limit_passages"] = 0;
+ if ( !isset($opts["limit_words"]) ) $opts["limit_words"] = 0;
+ if ( !isset($opts["around"]) ) $opts["around"] = 5;
+ if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false;
+ if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false;
+ if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false;
+ if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false;
+ if ( !isset($opts["query_mode"]) ) $opts["query_mode"] = false;
+ if ( !isset($opts["force_all_words"]) ) $opts["force_all_words"] = false;
+ if ( !isset($opts["start_passage_id"]) ) $opts["start_passage_id"] = 1;
+ if ( !isset($opts["load_files"]) ) $opts["load_files"] = false;
+ if ( !isset($opts["html_strip_mode"]) ) $opts["html_strip_mode"] = "index";
+ if ( !isset($opts["allow_empty"]) ) $opts["allow_empty"] = false;
+ if ( !isset($opts["passage_boundary"]) ) $opts["passage_boundary"] = "none";
+ if ( !isset($opts["emit_zones"]) ) $opts["emit_zones"] = false;
+ if ( !isset($opts["load_files_scattered"]) ) $opts["load_files_scattered"] = false;
+
+
+ /////////////////
+ // build request
+ /////////////////
+
+ // v.1.2 req
+ $flags = 1; // remove spaces
+ if ( $opts["exact_phrase"] ) $flags |= 2;
+ if ( $opts["single_passage"] ) $flags |= 4;
+ if ( $opts["use_boundaries"] ) $flags |= 8;
+ if ( $opts["weight_order"] ) $flags |= 16;
+ if ( $opts["query_mode"] ) $flags |= 32;
+ if ( $opts["force_all_words"] ) $flags |= 64;
+ if ( $opts["load_files"] ) $flags |= 128;
+ if ( $opts["allow_empty"] ) $flags |= 256;
+ if ( $opts["emit_zones"] ) $flags |= 512;
+ if ( $opts["load_files_scattered"] ) $flags |= 1024;
+ $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags
+ $req .= pack ( "N", strlen($index) ) . $index; // req index
+ $req .= pack ( "N", strlen($words) ) . $words; // req words
+
+ // options
+ $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"];
+ $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"];
+ $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"];
+ $req .= pack ( "NN", (int)$opts["limit"], (int)$opts["around"] );
+ $req .= pack ( "NNN", (int)$opts["limit_passages"], (int)$opts["limit_words"], (int)$opts["start_passage_id"] ); // v.1.2
+ $req .= pack ( "N", strlen($opts["html_strip_mode"]) ) . $opts["html_strip_mode"];
+ $req .= pack ( "N", strlen($opts["passage_boundary"]) ) . $opts["passage_boundary"];
+
+ // documents
+ $req .= pack ( "N", count($docs) );
+ foreach ( $docs as $doc )
+ {
+ assert ( is_string($doc) );
+ $req .= pack ( "N", strlen($doc) ) . $doc;
+ }
+
+ ////////////////////////////
+ // send query, get response
+ ////////////////////////////
+
+ $len = strlen($req);
+ $req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header
+ if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
+ !( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ) )
+ {
+ $this->_MBPop ();
+ return false;
+ }
+
+ //////////////////
+ // parse response
+ //////////////////
+
+ $pos = 0;
+ $res = array ();
+ $rlen = strlen($response);
+ for ( $i=0; $i<count($docs); $i++ )
+ {
+ list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) );
+ $pos += 4;
+
+ if ( $pos+$len > $rlen )
+ {
+ $this->_error = "incomplete reply";
+ $this->_MBPop ();
+ return false;
+ }
+ $res[] = $len ? substr ( $response, $pos, $len ) : "";
+ $pos += $len;
+ }
+
+ $this->_MBPop ();
+ return $res;
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ // keyword generation
+ /////////////////////////////////////////////////////////////////////////////
+
+ /// connect to searchd server, and generate keyword list for a given query
+ /// returns false on failure,
+ /// an array of words on success
+ function BuildKeywords ( $query, $index, $hits )
+ {
+ assert ( is_string($query) );
+ assert ( is_string($index) );
+ assert ( is_bool($hits) );
+
+ $this->_MBPush ();
+
+ if (!( $fp = $this->_Connect() ))
+ {
+ $this->_MBPop();
+ return false;
+ }
+
+ /////////////////
+ // build request
+ /////////////////
+
+ // v.1.0 req
+ $req = pack ( "N", strlen($query) ) . $query; // req query
+ $req .= pack ( "N", strlen($index) ) . $index; // req index
+ $req .= pack ( "N", (int)$hits );
+
+ ////////////////////////////
+ // send query, get response
+ ////////////////////////////
+
+ $len = strlen($req);
+ $req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header
+ if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
+ !( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ) )
+ {
+ $this->_MBPop ();
+ return false;
+ }
+
+ //////////////////
+ // parse response
+ //////////////////
+
+ $pos = 0;
+ $res = array ();
+ $rlen = strlen($response);
+ list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) );
+ $pos += 4;
+ for ( $i=0; $i<$nwords; $i++ )
+ {
+ list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
+ $tokenized = $len ? substr ( $response, $pos, $len ) : "";
+ $pos += $len;
+
+ list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
+ $normalized = $len ? substr ( $response, $pos, $len ) : "";
+ $pos += $len;
+
+ $res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized );
+
+ if ( $hits )
+ {
+ list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) );
+ $pos += 8;
+ $res [$i]["docs"] = $ndocs;
+ $res [$i]["hits"] = $nhits;
+ }
+
+ if ( $pos > $rlen )
+ {
+ $this->_error = "incomplete reply";
+ $this->_MBPop ();
+ return false;
+ }
+ }
+
+ $this->_MBPop ();
+ return $res;
+ }
+
+ function EscapeString ( $string )
+ {
+ $from = array ( '\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=' );
+ $to = array ( '\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=' );
+
+ return str_replace ( $from, $to, $string );
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // attribute updates
+ /////////////////////////////////////////////////////////////////////////////
+
+ /// batch update given attributes in given rows in given indexes
+ /// returns amount of updated documents (0 or more) on success, or -1 on failure
+ function UpdateAttributes ( $index, $attrs, $values, $mva=false )
+ {
+ // verify everything
+ assert ( is_string($index) );
+ assert ( is_bool($mva) );
+
+ assert ( is_array($attrs) );
+ foreach ( $attrs as $attr )
+ assert ( is_string($attr) );
+
+ assert ( is_array($values) );
+ foreach ( $values as $id=>$entry )
+ {
+ assert ( is_numeric($id) );
+ assert ( is_array($entry) );
+ assert ( count($entry)==count($attrs) );
+ foreach ( $entry as $v )
+ {
+ if ( $mva )
+ {
+ assert ( is_array($v) );
+ foreach ( $v as $vv )
+ assert ( is_int($vv) );
+ } else
+ assert ( is_int($v) );
+ }
+ }
+
+ // build request
+ $this->_MBPush ();
+ $req = pack ( "N", strlen($index) ) . $index;
+
+ $req .= pack ( "N", count($attrs) );
+ foreach ( $attrs as $attr )
+ {
+ $req .= pack ( "N", strlen($attr) ) . $attr;
+ $req .= pack ( "N", $mva ? 1 : 0 );
+ }
+
+ $req .= pack ( "N", count($values) );
+ foreach ( $values as $id=>$entry )
+ {
+ $req .= sphPackU64 ( $id );
+ foreach ( $entry as $v )
+ {
+ $req .= pack ( "N", $mva ? count($v) : $v );
+ if ( $mva )
+ foreach ( $v as $vv )
+ $req .= pack ( "N", $vv );
+ }
+ }
+
+ // connect, send query, get response
+ if (!( $fp = $this->_Connect() ))
+ {
+ $this->_MBPop ();
+ return -1;
+ }
+
+ $len = strlen($req);
+ $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header
+ if ( !$this->_Send ( $fp, $req, $len+8 ) )
+ {
+ $this->_MBPop ();
+ return -1;
+ }
+
+ if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) ))
+ {
+ $this->_MBPop ();
+ return -1;
+ }
+
+ // parse response
+ list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) );
+ $this->_MBPop ();
+ return $updated;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // persistent connections
+ /////////////////////////////////////////////////////////////////////////////
+
+ function Open()
+ {
+ if ( $this->_socket !== false )
+ {
+ $this->_error = 'already connected';
+ return false;
+ }
+ if ( !$fp = $this->_Connect() )
+ return false;
+
+ // command, command version = 0, body length = 4, body = 1
+ $req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 );
+ if ( !$this->_Send ( $fp, $req, 12 ) )
+ return false;
+
+ $this->_socket = $fp;
+ return true;
+ }
+
+ function Close()
+ {
+ if ( $this->_socket === false )
+ {
+ $this->_error = 'not connected';
+ return false;
+ }
+
+ fclose ( $this->_socket );
+ $this->_socket = false;
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // status
+ //////////////////////////////////////////////////////////////////////////
+
+ function Status ()
+ {
+ $this->_MBPush ();
+ if (!( $fp = $this->_Connect() ))
+ {
+ $this->_MBPop();
+ return false;
+ }
+
+ $req = pack ( "nnNN", SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1
+ if ( !( $this->_Send ( $fp, $req, 12 ) ) ||
+ !( $response = $this->_GetResponse ( $fp, VER_COMMAND_STATUS ) ) )
+ {
+ $this->_MBPop ();
+ return false;
+ }
+
+ $res = substr ( $response, 4 ); // just ignore length, error handling, etc
+ $p = 0;
+ list ( $rows, $cols ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
+
+ $res = array();
+ for ( $i=0; $i<$rows; $i++ )
+ for ( $j=0; $j<$cols; $j++ )
+ {
+ list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
+ $res[$i][] = substr ( $response, $p, $len ); $p += $len;
+ }
+
+ $this->_MBPop ();
+ return $res;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // flush
+ //////////////////////////////////////////////////////////////////////////
+
+ function FlushAttributes ()
+ {
+ $this->_MBPush ();
+ if (!( $fp = $this->_Connect() ))
+ {
+ $this->_MBPop();
+ return -1;
+ }
+
+ $req = pack ( "nnN", SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0 ); // len=0
+ if ( !( $this->_Send ( $fp, $req, 8 ) ) ||
+ !( $response = $this->_GetResponse ( $fp, VER_COMMAND_FLUSHATTRS ) ) )
+ {
+ $this->_MBPop ();
+ return -1;
+ }
+
+ $tag = -1;
+ if ( strlen($response)==4 )
+ list(,$tag) = unpack ( "N*", $response );
+ else
+ $this->_error = "unexpected response length";
+
+ $this->_MBPop ();
+ return $tag;
+ }
+}
+
+//
+// $Id: sphinxapi.php 3087 2012-01-30 23:07:35Z shodan $
+//
diff --git a/phpBB/includes/style/resource_locator.php b/phpBB/includes/style/resource_locator.php
index 8658fe4a36..4cf767c062 100644
--- a/phpBB/includes/style/resource_locator.php
+++ b/phpBB/includes/style/resource_locator.php
@@ -44,7 +44,7 @@ class phpbb_style_resource_locator implements phpbb_template_locator
* style directory, such as admin control panel templates.
* @var string
*/
- public $template_path = 'template/';
+ private $template_path;
/**
* Map from root index to handles to source template file paths.
@@ -64,6 +64,16 @@ class phpbb_style_resource_locator implements phpbb_template_locator
private $filenames = array();
/**
+ * Constructor.
+ *
+ * Sets default template path to template/.
+ */
+ public function __construct()
+ {
+ $this->set_default_template_path();
+ }
+
+ /**
* Sets the list of style paths
*
* These paths will be searched for style files in the provided order.
@@ -94,6 +104,31 @@ class phpbb_style_resource_locator implements phpbb_template_locator
}
/**
+ * Sets the location of templates directory within style directories.
+ *
+ * The location must be a relative path, with a trailing slash.
+ * Typically it is one directory level deep, e.g. "template/".
+ *
+ * @param string $template_path Relative path to templates directory within style directories
+ * @return null
+ */
+ public function set_template_path($template_path)
+ {
+ $this->template_path = $template_path;
+ }
+
+ /**
+ * Sets the location of templates directory within style directories
+ * to the default, which is "template/".
+ *
+ * @return null
+ */
+ public function set_default_template_path()
+ {
+ $this->template_path = 'template/';
+ }
+
+ /**
* {@inheritDoc}
*/
public function set_filenames(array $filename_array)
@@ -229,4 +264,85 @@ class phpbb_style_resource_locator implements phpbb_template_locator
// search failed
return $default_result;
}
+
+ /**
+ * Obtains filesystem path for a template file.
+ *
+ * The simplest use is specifying a single template file as a string
+ * in the first argument. This template file should be a basename
+ * of a template file in the selected style, or its parent styles
+ * if template inheritance is being utilized.
+ *
+ * Note: "selected style" is whatever style the style resource locator
+ * is configured for.
+ *
+ * The return value then will be a path, relative to the current
+ * directory or absolute, to the template file in the selected style
+ * or its closest parent.
+ *
+ * If the selected style does not have the template file being searched,
+ * (and if inheritance is involved, none of the parents have it either),
+ * false will be returned.
+ *
+ * Specifying true for $return_default will cause the function to
+ * return the first path which was checked for existence in the event
+ * that the template file was not found, instead of false.
+ * This is the path in the selected style itself, not any of its
+ * parents.
+ *
+ * $files can be given an array of templates instead of a single
+ * template. When given an array, the function will try to resolve
+ * each template in the array to a path, and will return the first
+ * path that exists, or false if none exist.
+ *
+ * If $files is an array and template inheritance is involved, first
+ * each of the files will be checked in the selected style, then each
+ * of the files will be checked in the immediate parent, and so on.
+ *
+ * If $return_full_path is false, then instead of returning a usable
+ * path (when the template is found) only the template's basename
+ * will be returned. This can be used to check which of the templates
+ * specified in $files exists. Naturally more than one template must
+ * be given in $files.
+ *
+ * This function works identically to get_first_file_location except
+ * it operates on a list of templates, not files. Practically speaking,
+ * the templates given in the first argument first are prepended with
+ * the template path (property in this class), then given to
+ * get_first_file_location for the rest of the processing.
+ *
+ * Templates given to this function can be relative paths for templates
+ * located in subdirectories of the template directories. The paths
+ * should be relative to the templates directory (template/ by default).
+ *
+ * @param string or array $files List of templates to locate. If there is only
+ * one template, $files can be a string to make code easier to read.
+ * @param bool $return_default Determines what to return if template does not
+ * exist. If true, function will return location where template is
+ * supposed to be. If false, function will return false.
+ * @param bool $return_full_path If true, function will return full path
+ * to template. If false, function will return template file name.
+ * This parameter can be used to check which one of set of template
+ * files is available.
+ * @return string or boolean Source template path if template exists or $return_default is
+ * true. False if template does not exist and $return_default is false
+ */
+ public function get_first_template_location($templates, $return_default = false, $return_full_path = true)
+ {
+ // add template path prefix
+ $files = array();
+ if (is_string($templates))
+ {
+ $files[] = $this->template_path . $templates;
+ }
+ else
+ {
+ foreach ($templates as $template)
+ {
+ $files[] = $this->template_path . $template;
+ }
+ }
+
+ return $this->get_first_file_location($files, $return_default, $return_full_path);
+ }
}
diff --git a/phpBB/includes/style/style.php b/phpBB/includes/style/style.php
index 36298b49ec..4703c3a219 100644
--- a/phpBB/includes/style/style.php
+++ b/phpBB/includes/style/style.php
@@ -91,16 +91,22 @@ class phpbb_style
{
$style_path = $this->user->style['style_path'];
$style_dirs = ($this->user->style['style_parent_id']) ? array_reverse(explode('/', $this->user->style['style_parent_tree'])) : array();
- $paths = array($this->get_style_path($style_path));
+
+ $names = array($style_path);
foreach ($style_dirs as $dir)
{
- $paths[] = $this->get_style_path($dir);
+ $names[] = $dir;
}
+ // Add 'all' path, used as last fallback path by events and extensions
+ //$names[] = 'all';
- // Add 'all' path, used as last fallback path by hooks and extensions
- $paths[] = $this->get_style_path('all');
+ $paths = array();
+ foreach ($names as $name)
+ {
+ $paths[] = $this->get_style_path($name);
+ }
- return $this->set_custom_style($style_path, $paths);
+ return $this->set_custom_style($style_path, $paths, $names);
}
/**
@@ -110,25 +116,38 @@ class phpbb_style
*
* @param string $name Name of style, used for cache prefix. Examples: "admin", "prosilver"
* @param array or string $paths Array of style paths, relative to current root directory
- * @param string $template_path Path to templates, relative to style directory. False if path should not be changed.
+ * @param array $names Array of names of templates in inheritance tree order, used by extensions. If empty, $name will be used.
+ * @param string $template_path Path to templates, relative to style directory. False if path should be set to default (templates/).
*/
- public function set_custom_style($name, $paths, $template_path = false)
+ public function set_custom_style($name, $paths, $names = array(), $template_path = false)
{
if (is_string($paths))
{
$paths = array($paths);
}
+ if (empty($names))
+ {
+ $names = array($name);
+ }
+ $this->names = $names;
+
$this->provider->set_styles($paths);
$this->locator->set_paths($this->provider);
- $this->template->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $name) . '_';
+ $this->template->set_style_names($names);
if ($template_path !== false)
{
- $this->template->template_path = $this->locator->template_path = $template_path;
+ $this->locator->set_template_path($template_path);
+ }
+ else
+ {
+ $this->locator->set_default_template_path();
}
+ $this->template->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $name) . '_';
+
return true;
}
diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php
index 82b301c1a2..fcdaf7abda 100644
--- a/phpBB/includes/template/compile.php
+++ b/phpBB/includes/template/compile.php
@@ -35,16 +35,23 @@ class phpbb_template_compile
/**
* Constructor.
*
- * @param bool @allow_php Whether PHP code will be allowed in templates (inline PHP code, PHP tag and INCLUDEPHP tag)
+ * @param bool $allow_php Whether PHP code will be allowed in templates (inline PHP code, PHP tag and INCLUDEPHP tag)
+ * @param array $style_names Name of style to which the template being compiled belongs and parents in style tree order
* @param phpbb_style_resource_locator $locator Resource locator
* @param string $phpbb_root_path Path to phpBB root directory
+ * @param phpbb_extension_manager $extension_manager Extension manager to use for finding template fragments in extensions; if null, template events will not be invoked
+ * @param phpbb_user $user Current user
*/
- public function __construct($allow_php, $locator, $phpbb_root_path)
+ public function __construct($allow_php, $style_names, $locator, $phpbb_root_path, $extension_manager = null, $user = null)
{
$this->filter_params = array(
'allow_php' => $allow_php,
+ 'style_names' => $style_names,
'locator' => $locator,
- 'phpbb_root_path' => $phpbb_root_path
+ 'phpbb_root_path' => $phpbb_root_path,
+ 'extension_manager' => $extension_manager,
+ 'user' => $user,
+ 'template_compile' => $this,
);
}
@@ -58,6 +65,9 @@ class phpbb_template_compile
*/
public function compile_file_to_file($source_file, $compiled_file)
{
+ $lock = new phpbb_lock_flock($compiled_file);
+ $lock->acquire();
+
$source_handle = @fopen($source_file, 'rb');
$destination_handle = @fopen($compiled_file, 'wb');
@@ -66,16 +76,15 @@ class phpbb_template_compile
return false;
}
- @flock($destination_handle, LOCK_EX);
-
$this->compile_stream_to_stream($source_handle, $destination_handle);
@fclose($source_handle);
- @flock($destination_handle, LOCK_UN);
@fclose($destination_handle);
phpbb_chmod($compiled_file, CHMOD_READ | CHMOD_WRITE);
+ $lock->release();
+
clearstatcache();
return true;
@@ -118,7 +127,7 @@ class phpbb_template_compile
*
* @param resource $source_stream Source stream
* @param resource $dest_stream Destination stream
- * @return void
+ * @return null
*/
private function compile_stream_to_stream($source_stream, $dest_stream)
{
diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php
index 66d28242a3..f73ad28ba1 100644
--- a/phpBB/includes/template/filter.php
+++ b/phpBB/includes/template/filter.php
@@ -88,6 +88,37 @@ class phpbb_template_filter extends php_user_filter
private $phpbb_root_path;
/**
+ * Name of the style that the template being compiled and/or rendered
+ * belongs to, and its parents, in inheritance tree order.
+ *
+ * Used to invoke style-specific template events.
+ *
+ * @var array
+ */
+ private $style_names;
+
+ /**
+ * Extension manager.
+ *
+ * @var phpbb_extension_manager
+ */
+ private $extension_manager;
+
+ /**
+ * Current user
+ *
+ * @var phpbb_user
+ */
+ private $user;
+
+ /**
+ * Template compiler.
+ *
+ * @var phpbb_template_compile
+ */
+ private $template_compile;
+
+ /**
* Stream filter
*
* Is invoked for evey chunk of the stream, allowing us
@@ -138,8 +169,10 @@ class phpbb_template_filter extends php_user_filter
/**
* Initializer, called on creation.
*
- * Get the allow_php option, root directory and locator from params,
+ * Get the allow_php option, style_names, root directory and locator from params,
* which are passed to stream_filter_append.
+ *
+ * @return boolean Returns true
*/
public function onCreate()
{
@@ -148,6 +181,13 @@ class phpbb_template_filter extends php_user_filter
$this->allow_php = $this->params['allow_php'];
$this->locator = $this->params['locator'];
$this->phpbb_root_path = $this->params['phpbb_root_path'];
+ $this->style_names = $this->params['style_names'];
+ $this->extension_manager = $this->params['extension_manager'];
+ if (isset($this->params['user']))
+ {
+ $this->user = $this->params['user'];
+ }
+ $this->template_compile = $this->params['template_compile'];
return true;
}
@@ -229,7 +269,9 @@ class phpbb_template_filter extends php_user_filter
}
/**
- * Callback for replacing matched tokens with PHP code
+ * Callback for replacing matched tokens with compiled template code.
+ *
+ * Compiled template code is an HTML stream with embedded PHP.
*
* @param array $matches Regular expression matches
* @return string compiled template code
@@ -317,6 +359,10 @@ class phpbb_template_filter extends php_user_filter
return '<!-- ENDPHP -->';
break;
+ case 'EVENT':
+ return '<?php ' . $this->compile_tag_event($matches[2]) . '?>';
+ break;
+
default:
return $matches[0];
break;
@@ -836,6 +882,97 @@ class phpbb_template_filter extends php_user_filter
}
/**
+ * Compile EVENT tag.
+ *
+ * $tag_args should be a single string identifying the event.
+ * The event name can contain letters, numbers and underscores only.
+ * If an invalid event name is specified, an E_USER_ERROR will be
+ * triggered.
+ *
+ * Event tags are only functional when the template engine has
+ * an instance of the extension manager. Extension manager would
+ * be called upon to find all extensions listening for the specified
+ * event, and to obtain additional template fragments. All such
+ * template fragments will be compiled and included in the generated
+ * compiled template code for the current template being compiled.
+ *
+ * The above means that whenever an extension is enabled or disabled,
+ * template cache should be cleared in order to update the compiled
+ * template code for the active set of template event listeners.
+ *
+ * This also means that extensions cannot return different template
+ * fragments at different times. Once templates are compiled, changing
+ * such template fragments would have no effect.
+ *
+ * @param string $tag_args EVENT tag arguments, as a string - for EVENT this is the event name
+ * @return string compiled template code
+ */
+ private function compile_tag_event($tag_args)
+ {
+ if (!preg_match('/^\w+$/', $tag_args))
+ {
+ // The event location is improperly formatted,
+ if ($this->user)
+ {
+ trigger_error($this->user->lang('ERR_TEMPLATE_EVENT_LOCATION', $tag_args), E_USER_ERROR);
+ }
+ else
+ {
+ trigger_error(sprintf('The specified template event location <em>[%s]</em> is improperly formatted.', $tag_args), E_USER_ERROR);
+ }
+ }
+ $location = $tag_args;
+
+ if ($this->extension_manager)
+ {
+ $finder = $this->extension_manager->get_finder();
+
+ $files = $finder
+ ->extension_prefix($location)
+ ->extension_suffix('.html')
+ ->extension_directory("/styles/all/template")
+ ->get_files();
+
+ foreach ($this->style_names as $style_name)
+ {
+ $more_files = $finder
+ ->extension_prefix($location)
+ ->extension_suffix('.html')
+ ->extension_directory("/styles/" . $style_name . "/template")
+ ->get_files();
+ if (!empty($more_files))
+ {
+ $files = array_merge($files, $more_files);
+ break;
+ }
+ }
+
+ $all_compiled = '';
+ foreach ($files as $file)
+ {
+ $compiled = $this->template_compile->compile_file($file);
+
+ if ($compiled === false)
+ {
+ if ($this->user)
+ {
+ trigger_error($this->user->lang('ERR_TEMPLATE_COMPILATION', phpbb_filter_root_path($file)), E_USER_ERROR);
+ }
+ else
+ {
+ trigger_error(sprintf('The file could not be compiled: %s', phpbb_filter_root_path($file)), E_USER_ERROR);
+ }
+ }
+
+ $all_compiled .= $compiled;
+ }
+ // Need spaces inside php tags as php cannot grok
+ // < ?php? > sans the spaces
+ return ' ?' . '>' . $all_compiled . '<?php ';
+ }
+ }
+
+ /**
* parse expression
* This is from Smarty
*/
diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php
index 01c79eec4e..f6fd20bcc2 100644
--- a/phpBB/includes/template/locator.php
+++ b/phpBB/includes/template/locator.php
@@ -39,7 +39,7 @@ interface phpbb_template_locator
* Sets the template filenames for handles. $filename_array
* should be a hash of handle => filename pairs.
*
- * @param array $filname_array Should be a hash of handle => filename pairs.
+ * @param array $filename_array Should be a hash of handle => filename pairs.
*/
public function set_filenames(array $filename_array);
@@ -66,7 +66,7 @@ interface phpbb_template_locator
* returns actually exists, it is faster than get_source_file_for_handle.
*
* Use get_source_file_for_handle to obtain the actual path that is
- * guaranteed to exist (which might come from the parent style
+ * guaranteed to exist (which might come from the parent style
* directory if primary style has parent styles).
*
* This function will trigger an error if the handle was never
@@ -99,12 +99,54 @@ interface phpbb_template_locator
public function get_source_file_for_handle($handle, $find_all = false);
/**
- * Locates source file path, accounting for styles tree and verifying that
- * the path exists.
+ * Obtains a complete filesystem path for a file in a style.
*
- * Unlike previous functions, this function works without template handle
- * and it can search for more than one file. If more than one file name is
- * specified, it will return location of file that it finds first.
+ * This function traverses the style tree (selected style and
+ * its parents in order, if inheritance is being used) and finds
+ * the first file on the filesystem matching specified relative path,
+ * or the first of the specified paths if more than one path is given.
+ *
+ * This function can be used to determine filesystem path of any
+ * file under any style, with the consequence being that complete
+ * relative to the style directory path must be provided as an argument.
+ *
+ * In particular, this function can be used to locate templates
+ * and javascript files.
+ *
+ * For locating templates get_first_template_location should be used
+ * as it prepends the configured template path to the template basename.
+ *
+ * Note: "selected style" is whatever style the style resource locator
+ * is configured for.
+ *
+ * The return value then will be a path, relative to the current
+ * directory or absolute, to the first existing file in the selected
+ * style or its closest parent.
+ *
+ * If the selected style does not have the file being searched,
+ * (and if inheritance is involved, none of the parents have it either),
+ * false will be returned.
+ *
+ * Multiple files can be specified, in which case the first file in
+ * the list that can be found on the filesystem is returned.
+ *
+ * If multiple files are specified and inheritance is involved,
+ * first each of the specified files is checked in the selected style,
+ * then each of the specified files is checked in the immediate parent,
+ * etc.
+ *
+ * Specifying true for $return_default will cause the function to
+ * return the first path which was checked for existence in the event
+ * that the template file was not found, instead of false.
+ * This is always a path in the selected style itself, not any of its
+ * parents.
+ *
+ * If $return_full_path is false, then instead of returning a usable
+ * path (when the file is found) the file's path relative to the style
+ * directory will be returned. This is the same path as was given to
+ * the function as a parameter. This can be used to check which of the
+ * files specified in $files exists. Naturally this requires passing
+ * more than one file in $files.
*
* @param array $files List of files to locate.
* @param bool $return_default Determines what to return if file does not
diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php
index 8a7dc6b2f3..bbec768613 100644
--- a/phpBB/includes/template/template.php
+++ b/phpBB/includes/template/template.php
@@ -75,10 +75,21 @@ class phpbb_template
private $locator;
/**
- * Location of templates directory within style directories
- * @var string
+ * Extension manager.
+ *
+ * @var phpbb_extension_manager
+ */
+ private $extension_manager;
+
+ /**
+ * Name of the style that the template being compiled and/or rendered
+ * belongs to, and its parents, in inheritance tree order.
+ *
+ * Used to invoke style-specific template events.
+ *
+ * @var array
*/
- public $template_path = 'template/';
+ private $style_names;
/**
* Constructor.
@@ -87,22 +98,23 @@ class phpbb_template
* @param user $user current user
* @param phpbb_template_locator $locator template locator
* @param phpbb_template_context $context template context
+ * @param phpbb_extension_manager $extension_manager extension manager, if null then template events will not be invoked
*/
- public function __construct($phpbb_root_path, $php_ext, $config, $user, phpbb_template_locator $locator, phpbb_template_context $context)
+ public function __construct($phpbb_root_path, $php_ext, $config, $user, phpbb_template_locator $locator, phpbb_template_context $context, phpbb_extension_manager $extension_manager = null)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$this->config = $config;
$this->user = $user;
$this->locator = $locator;
- $this->template_path = $this->locator->template_path;
$this->context = $context;
+ $this->extension_manager = $extension_manager;
}
/**
* Sets the template filenames for handles.
*
- * @param array $filname_array Should be a hash of handle => filename pairs.
+ * @param array $filename_array Should be a hash of handle => filename pairs.
*/
public function set_filenames(array $filename_array)
{
@@ -112,6 +124,18 @@ class phpbb_template
}
/**
+ * Sets the style names corresponding to style hierarchy being compiled
+ * and/or rendered.
+ *
+ * @param array $style_names List of style names in inheritance tree order
+ * @return null
+ */
+ public function set_style_names(array $style_names)
+ {
+ $this->style_names = $style_names;
+ }
+
+ /**
* Clears all variables and blocks assigned to this template.
*/
public function destroy()
@@ -289,7 +313,7 @@ class phpbb_template
return new phpbb_template_renderer_include($output_file, $this);
}
- $compile = new phpbb_template_compile($this->config['tpl_allow_php'], $this->locator, $this->phpbb_root_path);
+ $compile = new phpbb_template_compile($this->config['tpl_allow_php'], $this->style_names, $this->locator, $this->phpbb_root_path, $this->extension_manager, $this->user);
if ($compile->compile_file_to_file($source_file, $output_file) !== false)
{
@@ -459,74 +483,6 @@ class phpbb_template
}
/**
- * Obtains filesystem path for a template file.
- *
- * The simplest use is specifying a single template file as a string
- * in the first argument. This template file should be a basename
- * of a template file in the selected style, or its parent styles
- * if template inheritance is being utilized.
- *
- * Note: "selected style" is whatever style the style resource locator
- * is configured for.
- *
- * The return value then will be a path, relative to the current
- * directory or absolute, to the template file in the selected style
- * or its closest parent.
- *
- * If the selected style does not have the template file being searched,
- * (and if inheritance is involved, none of the parents have it either),
- * false will be returned.
- *
- * Specifying true for $return_default will cause the function to
- * return the first path which was checked for existence in the event
- * that the template file was not found, instead of false.
- * This is the path in the selected style itself, not any of its
- * parents.
- *
- * $files can be given an array of templates instead of a single
- * template. When given an array, the function will try to resolve
- * each template in the array to a path, and will return the first
- * path that exists, or false if none exist.
- *
- * If $return_full_path is false, then instead of returning a usable
- * path (when the template is found) only the template's basename
- * will be returned. This can be used to check which of the templates
- * specified in $files exists, provided different file names are
- * used for different templates.
- *
- * @param string or array $files List of templates to locate. If there is only
- * one template, $files can be a string to make code easier to read.
- * @param bool $return_default Determines what to return if template does not
- * exist. If true, function will return location where template is
- * supposed to be. If false, function will return false.
- * @param bool $return_full_path If true, function will return full path
- * to template. If false, function will return template file name.
- * This parameter can be used to check which one of set of template
- * files is available.
- * @return string or boolean Source template path if template exists or $return_default is
- * true. False if template does not exist and $return_default is false
- */
- public function locate($files, $return_default = false, $return_full_path = true)
- {
- // add template path prefix
- $templates = array();
- if (is_string($files))
- {
- $templates[] = $this->template_path . $files;
- }
- else
- {
- foreach ($files as $file)
- {
- $templates[] = $this->template_path . $file;
- }
- }
-
- // use resource locator to find files
- return $this->locator->get_first_file_location($templates, $return_default, $return_full_path);
- }
-
- /**
* Include JS file
*
* @param string $file file name
diff --git a/phpBB/includes/ucp/info/ucp_profile.php b/phpBB/includes/ucp/info/ucp_profile.php
index 201216e9fd..3581a7f533 100644
--- a/phpBB/includes/ucp/info/ucp_profile.php
+++ b/phpBB/includes/ucp/info/ucp_profile.php
@@ -19,7 +19,7 @@ class ucp_profile_info
'title' => 'UCP_PROFILE',
'version' => '1.0.0',
'modes' => array(
- 'profile_info' => array('title' => 'UCP_PROFILE_PROFILE_INFO', 'auth' => '', 'cat' => array('UCP_PROFILE')),
+ 'profile_info' => array('title' => 'UCP_PROFILE_PROFILE_INFO', 'auth' => 'acl_u_chgprofileinfo', 'cat' => array('UCP_PROFILE')),
'signature' => array('title' => 'UCP_PROFILE_SIGNATURE', 'auth' => 'acl_u_sig', 'cat' => array('UCP_PROFILE')),
'avatar' => array('title' => 'UCP_PROFILE_AVATAR', 'auth' => 'cfg_allow_avatar && (cfg_allow_avatar_local || cfg_allow_avatar_remote || cfg_allow_avatar_upload || cfg_allow_avatar_remote_upload)', 'cat' => array('UCP_PROFILE')),
'reg_details' => array('title' => 'UCP_PROFILE_REG_DETAILS', 'auth' => '', 'cat' => array('UCP_PROFILE')),
diff --git a/phpBB/includes/ucp/ucp_groups.php b/phpBB/includes/ucp/ucp_groups.php
index 9652986cf2..b9a06bc3b4 100644
--- a/phpBB/includes/ucp/ucp_groups.php
+++ b/phpBB/includes/ucp/ucp_groups.php
@@ -25,7 +25,7 @@ class ucp_groups
function main($id, $mode)
{
- global $config, $phpbb_root_path, $phpEx;
+ global $config, $phpbb_root_path, $phpEx, $phpbb_admin_path;
global $db, $user, $auth, $cache, $template;
global $request;
@@ -438,7 +438,7 @@ class ucp_groups
$group_name = $group_row['group_name'];
$group_type = $group_row['group_type'];
- $avatar_img = (!empty($group_row['group_avatar'])) ? get_user_avatar($group_row['group_avatar'], $group_row['group_avatar_type'], $group_row['group_avatar_width'], $group_row['group_avatar_height'], 'GROUP_AVATAR') : '<img src="' . $phpbb_root_path . 'adm/images/no_avatar.gif" alt="" />';
+ $avatar_img = (!empty($group_row['group_avatar'])) ? get_user_avatar($group_row['group_avatar'], $group_row['group_avatar_type'], $group_row['group_avatar_width'], $group_row['group_avatar_height'], 'GROUP_AVATAR') : '<img src="' . $phpbb_admin_path . 'images/no_avatar.gif" alt="" />';
$template->assign_vars(array(
'GROUP_NAME' => ($group_type == GROUP_SPECIAL) ? $user->lang['G_' . $group_name] : $group_name,
@@ -618,7 +618,7 @@ class ucp_groups
foreach ($test_variables as $test => $type)
{
- if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test]))
+ if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || isset($group_attributes['group_avatar']) && strpos($test, 'avatar') === 0))
{
settype($submit_ary[$test], $type);
$group_attributes['group_' . $test] = $group_row['group_' . $test] = $submit_ary[$test];
@@ -730,7 +730,7 @@ class ucp_groups
'GROUP_CLOSED' => $type_closed,
'GROUP_HIDDEN' => $type_hidden,
- 'U_SWATCH' => append_sid("{$phpbb_root_path}adm/swatch.$phpEx", 'form=ucp&amp;name=group_colour'),
+ 'U_SWATCH' => append_sid("{$phpbb_admin_path}swatch.$phpEx", 'form=ucp&amp;name=group_colour'),
'S_UCP_ACTION' => $this->u_action . "&amp;action=$action&amp;g=$group_id",
'L_AVATAR_EXPLAIN' => phpbb_avatar_explanation_string(),
));
diff --git a/phpBB/includes/ucp/ucp_pm_viewmessage.php b/phpBB/includes/ucp/ucp_pm_viewmessage.php
index c85b05f144..a1001cfa74 100644
--- a/phpBB/includes/ucp/ucp_pm_viewmessage.php
+++ b/phpBB/includes/ucp/ucp_pm_viewmessage.php
@@ -241,7 +241,6 @@ function view_message($id, $mode, $folder_id, $msg_id, $folder, $message_row)
'U_ICQ' => ($user_info['user_icq']) ? 'http://www.icq.com/people/' . urlencode($user_info['user_icq']) . '/' : '',
'U_AIM' => ($user_info['user_aim'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&amp;action=aim&amp;u=' . $author_id) : '',
'U_YIM' => ($user_info['user_yim']) ? 'http://edit.yahoo.com/config/send_webmesg?.target=' . urlencode($user_info['user_yim']) . '&amp;.src=pg' : '',
- 'U_MSN' => ($user_info['user_msnm'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&amp;action=msnm&amp;u=' . $author_id) : '',
'U_JABBER' => ($user_info['user_jabber'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&amp;action=jabber&amp;u=' . $author_id) : '',
'U_DELETE' => ($auth->acl_get('u_pm_delete')) ? "$url&amp;mode=compose&amp;action=delete&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] : '',
diff --git a/phpBB/includes/ucp/ucp_prefs.php b/phpBB/includes/ucp/ucp_prefs.php
index 2228bc7931..23892c2c8c 100644
--- a/phpBB/includes/ucp/ucp_prefs.php
+++ b/phpBB/includes/ucp/ucp_prefs.php
@@ -132,6 +132,36 @@ class ucp_prefs
$dateformat_options .= '>' . $user->lang['CUSTOM_DATEFORMAT'] . '</option>';
$timezone_selects = phpbb_timezone_select($user, $data['tz'], true);
+
+ // check if there are any user-selectable languages
+ $sql = 'SELECT COUNT(lang_id) as languages_count
+ FROM ' . LANG_TABLE;
+ $result = $db->sql_query($sql);
+ if ($db->sql_fetchfield('languages_count') > 1)
+ {
+ $s_more_languages = true;
+ }
+ else
+ {
+ $s_more_languages = false;
+ }
+ $db->sql_freeresult($result);
+
+ // check if there are any user-selectable styles
+ $sql = 'SELECT COUNT(style_id) as styles_count
+ FROM ' . STYLES_TABLE . '
+ WHERE style_active = 1';
+ $result = $db->sql_query($sql);
+ if ($db->sql_fetchfield('styles_count') > 1)
+ {
+ $s_more_styles = true;
+ }
+ else
+ {
+ $s_more_styles = false;
+ }
+ $db->sql_freeresult($result);
+
$template->assign_vars(array(
'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
@@ -152,6 +182,9 @@ class ucp_prefs
'DEFAULT_DATEFORMAT' => $config['default_dateformat'],
'A_DEFAULT_DATEFORMAT' => addslashes($config['default_dateformat']),
+ 'S_MORE_LANGUAGES' => $s_more_languages,
+ 'S_MORE_STYLES' => $s_more_styles,
+
'S_LANG_OPTIONS' => language_select($data['lang']),
'S_STYLE_OPTIONS' => ($config['override_user_style']) ? '' : style_select($data['style']),
'S_TZ_OPTIONS' => $timezone_selects['tz_select'],
diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php
index 89bf20a30f..c1ad9955b6 100644
--- a/phpBB/includes/ucp/ucp_profile.php
+++ b/phpBB/includes/ucp/ucp_profile.php
@@ -251,6 +251,11 @@ class ucp_profile
break;
case 'profile_info':
+ // Do not display profile information panel if not authed to do so
+ if (!$auth->acl_get('u_chgprofileinfo'))
+ {
+ trigger_error('NO_AUTH_PROFILEINFO');
+ }
include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
@@ -261,7 +266,6 @@ class ucp_profile
$data = array(
'icq' => request_var('icq', $user->data['user_icq']),
'aim' => request_var('aim', $user->data['user_aim']),
- 'msn' => request_var('msn', $user->data['user_msnm']),
'yim' => request_var('yim', $user->data['user_yim']),
'jabber' => utf8_normalize_nfc(request_var('jabber', $user->data['user_jabber'], true)),
'website' => request_var('website', $user->data['user_website']),
@@ -294,7 +298,6 @@ class ucp_profile
array('string', true, 3, 15),
array('match', true, '#^[0-9]+$#i')),
'aim' => array('string', true, 3, 255),
- 'msn' => array('string', true, 5, 255),
'jabber' => array(
array('string', true, 5, 255),
array('jabber')),
@@ -346,7 +349,6 @@ class ucp_profile
$sql_ary = array(
'user_icq' => $data['icq'],
'user_aim' => $data['aim'],
- 'user_msnm' => $data['msn'],
'user_yim' => $data['yim'],
'user_jabber' => $data['jabber'],
'user_website' => $data['website'],
@@ -418,7 +420,6 @@ class ucp_profile
'ICQ' => $data['icq'],
'YIM' => $data['yim'],
'AIM' => $data['aim'],
- 'MSN' => $data['msn'],
'JABBER' => $data['jabber'],
'WEBSITE' => $data['website'],
'LOCATION' => $data['location'],