From cbf4544887c7f496f2b2312727fe7bcb64a6d6c8 Mon Sep 17 00:00:00 2001 From: Alexander Kampmann Date: Thu, 5 Apr 2012 13:27:09 +0200 Subject: added index to config and pconfig table changed config.php to use REPLACE instead of INSERT, this removes one db hit. --- include/config.php | 306 +++++++++++++++++++++++++++-------------------------- 1 file changed, 154 insertions(+), 152 deletions(-) mode change 100755 => 100644 include/config.php (limited to 'include') diff --git a/include/config.php b/include/config.php old mode 100755 new mode 100644 index 2cddda0b8..f93cb52c9 --- a/include/config.php +++ b/include/config.php @@ -1,222 +1,224 @@ config[$k] = $rr['v']; - } else { - $a->config[$family][$k] = $rr['v']; + function load_config($family) { + global $a; + $r = q("SELECT * FROM `config` WHERE `cat` = '%s'", + dbesc($family) + ); + if(count($r)) { + foreach($r as $rr) { + $k = $rr['k']; + if ($rr['cat'] === 'config') { + $a->config[$k] = $rr['v']; + } else { + $a->config[$family][$k] = $rr['v']; + } } } } -}} - -// get a particular config variable given the family name -// and key. Returns false if not set. -// $instore is only used by the set_config function -// to determine if the key already exists in the DB -// If a key is found in the DB but doesn't exist in -// local config cache, pull it into the cache so we don't have -// to hit the DB again for this item. +} +/** + * get a particular config variable given the family name + * and key. Returns false if not set. + * + * If a key is found in the DB but doesn't exist in + * local config cache, pull it into the cache so we don't have + *to hit the DB again for this item. + */ if(! function_exists('get_config')) { -function get_config($family, $key, $instore = false) { + function get_config($family, $key) { + + global $a; - global $a; - if(! $instore) { if(isset($a->config[$family][$key])) { if($a->config[$family][$key] === '!!') { return false; } return $a->config[$family][$key]; } + $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($family), + dbesc($key) + ); + if(count($ret)) { + // manage array value + $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); + $a->config[$family][$key] = $val; + return $val; + } + else { + $a->config[$family][$key] = '!!'; + } + return false; } - $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($family), - dbesc($key) - ); - if(count($ret)) { - // manage array value - $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); - $a->config[$family][$key] = $val; - return $val; - } - else { - $a->config[$family][$key] = '!!'; - } - return false; -}} - -// Store a config value ($value) in the category ($family) -// under the key ($key) -// Return the value, or false if the database update failed +} +/** + * Store a config value ($value) in the category ($family) + * under the key ($key) + * + * Return the value, or false if the database update failed + */ if(! function_exists('set_config')) { -function set_config($family,$key,$value) { - global $a; - - // manage array value - $dbvalue = (is_array($value)?serialize($value):$value); + function set_config($family,$key,$value) { + global $a; + + // manage array value + $dbvalue = (is_array($value)?serialize($value):$value); - if(get_config($family,$key,true) === false) { $a->config[$family][$key] = $value; - $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ", - dbesc($family), - dbesc($key), - dbesc($dbvalue) + $ret = q("REPLACE INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ", + dbesc($family), + dbesc($key), + dbesc($dbvalue) ); - if($ret) + if($ret) { return $value; + } return $ret; - } - - $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($dbvalue), - dbesc($family), - dbesc($key) - ); - - $a->config[$family][$key] = $value; - if($ret) - return $value; - return $ret; -}} + } +} if(! function_exists('load_pconfig')) { -function load_pconfig($uid,$family) { - global $a; - $r = q("SELECT * FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d", - dbesc($family), - intval($uid) - ); - if(count($r)) { - foreach($r as $rr) { - $k = $rr['k']; - $a->config[$uid][$family][$k] = $rr['v']; + function load_pconfig($uid,$family) { + global $a; + $r = q("SELECT * FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d", + dbesc($family), + intval($uid) + ); + if(count($r)) { + foreach($r as $rr) { + $k = $rr['k']; + $a->config[$uid][$family][$k] = $rr['v']; + } } } -}} - +} +/** + * get a particular user-specific config variable given the family name, + * the user id and key. Returns false if not set. + * + * If a key is found in the DB but doesn't exist in + * local config cache, pull it into the cache so we don't have + * to hit the DB again for this item. + */ if(! function_exists('get_pconfig')) { -function get_pconfig($uid,$family, $key, $instore = false) { + function get_pconfig($uid,$family, $key) { + + global $a; - global $a; - if(! $instore) { if(isset($a->config[$uid][$family][$key])) { if($a->config[$uid][$family][$key] === '!!') { return false; } return $a->config[$uid][$family][$key]; } - } - $ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", - intval($uid), - dbesc($family), - dbesc($key) - ); - if(count($ret)) { - $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); - $a->config[$uid][$family][$key] = $val; - return $val; - } - else { - $a->config[$uid][$family][$key] = '!!'; + $ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + intval($uid), + dbesc($family), + dbesc($key) + ); + + if(count($ret)) { + $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); + $a->config[$uid][$family][$key] = $val; + return $val; + } + else { + $a->config[$uid][$family][$key] = '!!'; + } + return false; } - return false; -}} +} +/** + * Delete a value from config. This function + * deletes both: db value and cache entry. + */ if(! function_exists('del_config')) { -function del_config($family,$key) { - - global $a; - if(x($a->config[$family],$key)) - unset($a->config[$family][$key]); - $ret = q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($family), - dbesc($key) - ); - return $ret; -}} - - + function del_config($family,$key) { + + global $a; + if(x($a->config[$family],$key)) + unset($a->config[$family][$key]); + $ret = q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($family), + dbesc($key) + ); + return $ret; + } +} -// Same as above functions except these are for personal config storage and take an -// additional $uid argument. +/** + * Store a user-specific config value ($value) for user $uid in the category ($family) + * under the key ($key). + * + * Return the value, or false if the database update failed + */ if(! function_exists('set_pconfig')) { -function set_pconfig($uid,$family,$key,$value) { + function set_pconfig($uid,$family,$key,$value) { - global $a; + global $a; + + // manage array value + $dbvalue = (is_array($value)?serialize($value):$value); - // manage array value - $dbvalue = (is_array($value)?serialize($value):$value); - if(get_pconfig($uid,$family,$key,true) === false) { $a->config[$uid][$family][$key] = $value; - $ret = q("INSERT INTO `pconfig` ( `uid`, `cat`, `k`, `v` ) VALUES ( %d, '%s', '%s', '%s' ) ", - intval($uid), - dbesc($family), - dbesc($key), - dbesc($dbvalue) + $ret = q("REPLACE INTO `pconfig` ( `uid`, `cat`, `k`, `v` ) VALUES ( %d, '%s', '%s', '%s' ) ", + intval($uid), + dbesc($family), + dbesc($key), + dbesc($dbvalue) ); - if($ret) + if($ret) { return $value; + } return $ret; - } - $ret = q("UPDATE `pconfig` SET `v` = '%s' WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($dbvalue), - intval($uid), - dbesc($family), - dbesc($key) - ); - - $a->config[$uid][$family][$key] = $value; - if($ret) - return $value; - return $ret; -}} + } +} if(! function_exists('del_pconfig')) { -function del_pconfig($uid,$family,$key) { - - global $a; - if(x($a->config[$uid][$family],$key)) - unset($a->config[$uid][$family][$key]); - $ret = q("DELETE FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", - intval($uid), - dbesc($family), - dbesc($key) - ); - return $ret; -}} + function del_pconfig($uid,$family,$key) { + + global $a; + if(x($a->config[$uid][$family],$key)) + unset($a->config[$uid][$family][$key]); + $ret = q("DELETE FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + intval($uid), + dbesc($family), + dbesc($key) + ); + return $ret; + } +} -- cgit v1.2.3 From 221726392e6ec836623f83320c3ff9afdc876045 Mon Sep 17 00:00:00 2001 From: friendica Date: Thu, 5 Apr 2012 18:44:36 -0700 Subject: pass autoname(0) test --- include/text.php | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/text.php b/include/text.php index 5456b5ad3..29c781030 100644 --- a/include/text.php +++ b/include/text.php @@ -80,6 +80,9 @@ function escape_tags($string) { if(! function_exists('autoname')) { function autoname($len) { + if(! $len) + return ''; + $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); if(mt_rand(0,5) == 4) $vowels[] = 'y'; -- cgit v1.2.3 From 665b2c8107e372fd2703f126b8c9db5c9719a36b Mon Sep 17 00:00:00 2001 From: friendica Date: Fri, 6 Apr 2012 03:10:46 -0700 Subject: revert config changes, we're getting duplicate keys --- include/config.php | 298 ++++++++++++++++++++++++++--------------------------- 1 file changed, 147 insertions(+), 151 deletions(-) mode change 100644 => 100755 include/config.php (limited to 'include') diff --git a/include/config.php b/include/config.php old mode 100644 new mode 100755 index c999b76f2..4cff38090 --- a/include/config.php +++ b/include/config.php @@ -1,87 +1,80 @@ config[$k] = $rr['v']; - } else { - $a->config[$family][$k] = $rr['v']; - } +function load_config($family) { + global $a; + $r = q("SELECT * FROM `config` WHERE `cat` = '%s'", + dbesc($family) + ); + if(count($r)) { + foreach($r as $rr) { + $k = $rr['k']; + if ($rr['cat'] === 'config') { + $a->config[$k] = $rr['v']; + } else { + $a->config[$family][$k] = $rr['v']; } } } -} +}} -/** - * get a particular config variable given the family name - * and key. Returns false if not set. - * - * If a key is found in the DB but doesn't exist in - * local config cache, pull it into the cache so we don't have - *to hit the DB again for this item. - */ -if(! function_exists('get_config')) { - function get_config($family, $key) { +// get a particular config variable given the family name +// and key. Returns false if not set. +// $instore is only used by the set_config function +// to determine if the key already exists in the DB +// If a key is found in the DB but doesn't exist in +// local config cache, pull it into the cache so we don't have +// to hit the DB again for this item. - global $a; +if(! function_exists('get_config')) { +function get_config($family, $key, $instore = false) { + global $a; + if(! $instore) { if(isset($a->config[$family][$key])) { if($a->config[$family][$key] === '!!') { return false; } return $a->config[$family][$key]; } - $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($family), - dbesc($key) - ); - if(count($ret)) { - // manage array value - $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); - $a->config[$family][$key] = $val; - return $val; - } - else { - $a->config[$family][$key] = '!!'; - } - return false; } -} + $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($family), + dbesc($key) + ); + if(count($ret)) { + // manage array value + $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); + $a->config[$family][$key] = $val; + return $val; + } + else { + $a->config[$family][$key] = '!!'; + } + return false; +}} -/** - * Store a config value ($value) in the category ($family) - * under the key ($key) - * - * Return the value, or false if the database update failed - */ -if(! function_exists('set_config')) { +// Store a config value ($value) in the category ($family) +// under the key ($key) +// Return the value, or false if the database update failed +if(! function_exists('set_config')) { function set_config($family,$key,$value) { global $a; @@ -89,139 +82,142 @@ function set_config($family,$key,$value) { $dbvalue = (is_array($value)?serialize($value):$value); $dbvalue = (is_bool($value) ? intval($value) : $value); + if(get_config($family,$key,true) === false) { $a->config[$family][$key] = $value; - $ret = q("REPLACE INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ", - dbesc($family), - dbesc($key), - dbesc($dbvalue) + $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ", + dbesc($family), + dbesc($key), + dbesc($dbvalue) ); - if($ret) { + if($ret) return $value; - } return $ret; - } -} + + $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($dbvalue), + dbesc($family), + dbesc($key) + ); + + $a->config[$family][$key] = $value; + + if($ret) + return $value; + return $ret; +}} if(! function_exists('load_pconfig')) { - function load_pconfig($uid,$family) { - global $a; - $r = q("SELECT * FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d", - dbesc($family), - intval($uid) - ); - if(count($r)) { - foreach($r as $rr) { - $k = $rr['k']; - $a->config[$uid][$family][$k] = $rr['v']; - } +function load_pconfig($uid,$family) { + global $a; + $r = q("SELECT * FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d", + dbesc($family), + intval($uid) + ); + if(count($r)) { + foreach($r as $rr) { + $k = $rr['k']; + $a->config[$uid][$family][$k] = $rr['v']; } } -} +}} -/** - * get a particular user-specific config variable given the family name, - * the user id and key. Returns false if not set. - * - * If a key is found in the DB but doesn't exist in - * local config cache, pull it into the cache so we don't have - * to hit the DB again for this item. - */ -if(! function_exists('get_pconfig')) { - function get_pconfig($uid,$family, $key) { - global $a; +if(! function_exists('get_pconfig')) { +function get_pconfig($uid,$family, $key, $instore = false) { + global $a; + if(! $instore) { if(isset($a->config[$uid][$family][$key])) { if($a->config[$uid][$family][$key] === '!!') { return false; } return $a->config[$uid][$family][$key]; } + } + $ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + intval($uid), + dbesc($family), + dbesc($key) + ); - $ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", - intval($uid), - dbesc($family), - dbesc($key) - ); - - if(count($ret)) { - $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); - $a->config[$uid][$family][$key] = $val; - return $val; - } - else { - $a->config[$uid][$family][$key] = '!!'; - } - return false; + if(count($ret)) { + $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); + $a->config[$uid][$family][$key] = $val; + return $val; + } + else { + $a->config[$uid][$family][$key] = '!!'; } -} + return false; +}} -/** - * Delete a value from config. This function - * deletes both: db value and cache entry. - */ if(! function_exists('del_config')) { - function del_config($family,$key) { - - global $a; - if(x($a->config[$family],$key)) - unset($a->config[$family][$key]); - $ret = q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($family), - dbesc($key) - ); - return $ret; - } -} +function del_config($family,$key) { + global $a; + if(x($a->config[$family],$key)) + unset($a->config[$family][$key]); + $ret = q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($family), + dbesc($key) + ); + return $ret; +}} -/** - * Store a user-specific config value ($value) for user $uid in the category ($family) - * under the key ($key). - * - * Return the value, or false if the database update failed - */ -if(! function_exists('set_pconfig')) { - function set_pconfig($uid,$family,$key,$value) { - global $a; - // manage array value - $dbvalue = (is_array($value)?serialize($value):$value); - $dbvalue = (is_bool($value)?serialize($value):$value); +// Same as above functions except these are for personal config storage and take an +// additional $uid argument. + +if(! function_exists('set_pconfig')) { +function set_pconfig($uid,$family,$key,$value) { + + global $a; + // manage array value + $dbvalue = (is_array($value)?serialize($value):$value); + if(get_pconfig($uid,$family,$key,true) === false) { $a->config[$uid][$family][$key] = $value; - $ret = q("REPLACE INTO `pconfig` ( `uid`, `cat`, `k`, `v` ) VALUES ( %d, '%s', '%s', '%s' ) ", - intval($uid), - dbesc($family), - dbesc($key), - dbesc($dbvalue) + $ret = q("INSERT INTO `pconfig` ( `uid`, `cat`, `k`, `v` ) VALUES ( %d, '%s', '%s', '%s' ) ", + intval($uid), + dbesc($family), + dbesc($key), + dbesc($dbvalue) ); - if($ret) { + if($ret) return $value; - } return $ret; - } -} + $ret = q("UPDATE `pconfig` SET `v` = '%s' WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($dbvalue), + intval($uid), + dbesc($family), + dbesc($key) + ); + + $a->config[$uid][$family][$key] = $value; + + if($ret) + return $value; + return $ret; +}} if(! function_exists('del_pconfig')) { - function del_pconfig($uid,$family,$key) { - - global $a; - if(x($a->config[$uid][$family],$key)) - unset($a->config[$uid][$family][$key]); - $ret = q("DELETE FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", - intval($uid), - dbesc($family), - dbesc($key) - ); - return $ret; - } -} +function del_pconfig($uid,$family,$key) { + + global $a; + if(x($a->config[$uid][$family],$key)) + unset($a->config[$uid][$family][$key]); + $ret = q("DELETE FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + intval($uid), + dbesc($family), + dbesc($key) + ); + return $ret; +}} -- cgit v1.2.3 From 13dfb4012894c3bd724f12d75f4de7c1cce9529a Mon Sep 17 00:00:00 2001 From: friendica Date: Fri, 6 Apr 2012 05:11:09 -0700 Subject: fix messed up config tables w/ duplicate entries --- include/plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/plugin.php b/include/plugin.php index e37ae8435..25fd32b4f 100644 --- a/include/plugin.php +++ b/include/plugin.php @@ -34,7 +34,7 @@ function install_plugin($plugin){ ); } else { - logger("Addons: FAILED installing " . $plugin); +// logger("Addons: FAILED installing " . $plugin); } }} -- cgit v1.2.3 From ecabe1d505464577fdc3d3ff0090371c8ca0cf1e Mon Sep 17 00:00:00 2001 From: friendica Date: Fri, 6 Apr 2012 05:21:42 -0700 Subject: ignore removed plugins --- include/plugin.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/plugin.php b/include/plugin.php index 25fd32b4f..8196e8756 100644 --- a/include/plugin.php +++ b/include/plugin.php @@ -17,7 +17,12 @@ function uninstall_plugin($plugin){ }} if (! function_exists('install_plugin')){ -function install_plugin($plugin){ +function install_plugin($plugin) { + + // silently fail if plugin was removed + + if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) + return false; logger("Addons: installing " . $plugin); $t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); @include_once('addon/' . $plugin . '/' . $plugin . '.php'); @@ -32,9 +37,11 @@ function install_plugin($plugin){ intval($t), $plugin_admin ); + return true; } else { -// logger("Addons: FAILED installing " . $plugin); + logger("Addons: FAILED installing " . $plugin); + return false; } }} -- cgit v1.2.3 From 3a1c78bd75213e5ed3ad5f5ed4a1349fcf7ce4d5 Mon Sep 17 00:00:00 2001 From: friendica Date: Sat, 7 Apr 2012 01:04:45 -0700 Subject: some work on tests --- include/text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/text.php b/include/text.php index 29c781030..aced9e0a8 100644 --- a/include/text.php +++ b/include/text.php @@ -80,7 +80,7 @@ function escape_tags($string) { if(! function_exists('autoname')) { function autoname($len) { - if(! $len) + if($len <= 0) return ''; $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); -- cgit v1.2.3 From 1440786c319b25f870ef1f85dba7aaf50e49f842 Mon Sep 17 00:00:00 2001 From: friendica Date: Sat, 7 Apr 2012 02:31:41 -0700 Subject: ignore utf8 offset error for unit tests --- include/template_processor.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/template_processor.php b/include/template_processor.php index 4c317efe1..46252c355 100644 --- a/include/template_processor.php +++ b/include/template_processor.php @@ -13,12 +13,14 @@ var $debug=false; private function _preg_error(){ + switch(preg_last_error()){ case PREG_INTERNAL_ERROR: echo('PREG_INTERNAL_ERROR'); break; case PREG_BACKTRACK_LIMIT_ERROR: echo('PREG_BACKTRACK_LIMIT_ERROR'); break; case PREG_RECURSION_LIMIT_ERROR: echo('PREG_RECURSION_LIMIT_ERROR'); break; case PREG_BAD_UTF8_ERROR: echo('PREG_BAD_UTF8_ERROR'); break; - case PREG_BAD_UTF8_OFFSET_ERROR: echo('PREG_BAD_UTF8_OFFSET_ERROR'); break; +// This is only valid for php > 5.3, not certain how to code around it for unit tests +// case PREG_BAD_UTF8_OFFSET_ERROR: echo('PREG_BAD_UTF8_OFFSET_ERROR'); break; default: //die("Unknown preg error."); return; -- cgit v1.2.3