function controller_settings(&$args, $output = "inline") { $cfg = $this->root->cfg->FlattenConfig($this->root->cfg->LoadServers($this->root->locations["config"] . "/servers.ini", false)); $vars["tfdev"] = !empty($_COOKIE["tf-dev"]) ? json_decode($_COOKIE["tf-dev"], true) : array(); if (!empty($args["clear"])) { Logger::Info("Cleared dev settings"); setcookie("tf-dev", NULL, 0, "/"); $vars["tfdev"] = array(); $this->root->cfg->servers = $cfg; } if (!empty($args["settings"])) { $diff_orig = array_diff_assoc_recursive($args["settings"], $cfg); $diff_curr = array_diff_assoc_recursive($args["settings"], $this->root->cfg->FlattenConfig($this->root->cfg->servers)); $vars["tfdev"]["serveroverrides"] = $diff_orig; setcookie("tf-dev", json_encode($vars["tfdev"]), time() + 86400 * 365, "/"); // dev cookie persists for a year if (!empty($diff_curr)) { foreach ($diff_curr as $setting => $value) { Logger::Info("Override setting: {$setting} = '{$value}'"); $this->root->cfg->AppendSetting($this->root->cfg->servers, $setting, $value); } } else { Logger::Error("No config differences!"); } } $vars["settings"] = $this->root->cfg->FlattenConfig($this->root->cfg->servers); $vars["container"] = $output == "html"; $ret = $this->GetComponentResponse("./settings.tpl", $vars); if ($output == "ajax") { $ret = array("tf_debug_tab_settings" => $ret); } return $ret; }
function __construct($name, $cfg, $lazy = false) { parent::__construct($name, $cfg, $lazy); // default if not set $timeout = $this->cfg["timeout"] > 0 ? $this->cfg["timeout"] : 0; $persist = $this->cfg["persist"] ? true : false; $this->cache = new Memcache(); // add to the list if (!empty($this->cfg["servers"])) { // If it's a string, assume it's space-separated if (is_string($this->cfg["servers"])) { $this->cfg["servers"] = explode(" ", $this->cfg["servers"]); } foreach ($this->cfg["servers"] as $server) { list($host, $port) = explode(":", $server); if (empty($port)) { $port = 11211; } // default memcache port //Profiler::StartTimer("MemcacheCache::addServers()"); $this->servers[$host] = $params = array("port" => $port, "timeout" => $timeout, "persist" => $persist); Logger::Info("Added memcache server - %s:%s (persist=%d)", $host, $params["port"], $params["persist"]); $this->cache->addServer($host, $params["port"], $params["persist"]); //Profiler::StopTimer("MemcacheCache::addServers()"); } } }
public static function lookup($hostname) { self::init(); Profiler::StartTimer("DNSResolver::lookup()", 2); $data = DataManager::singleton(); $records = $apc = NULL; $cachekey = "dnsresolver.lookup.{$hostname}"; if (self::$cache && !empty($data->caches["apc"]) && $data->caches["apc"]["default"]->enabled) { $apc = $data->caches["apc"]["default"]; $cached = $apc->get($cachekey); if ($cached !== false) { $records = unserialize($cached); Logger::Info("DNSResolver: found '{$hostname}' in APC cache"); } } if ($records === NULL) { Logger::Info("DNSResolver: Looking up '{$hostname}'"); foreach (self::$search as $suffix) { $fqdn = $hostname . (!empty($suffix) ? "." . $suffix : ""); $records = dns_get_record($fqdn, DNS_A); if (!empty($records)) { break; } } if (self::$cache && !empty($records) && $apc !== NULL && $apc->enabled) { $ttl = any(self::$ttl, $records[0]["ttl"]); $apc->set($cachekey, serialize($records), array("lifetime" => $ttl)); } } Profiler::StopTimer("DNSResolver::lookup()"); return $records; }
function Query($queryid, $query, $args = NULL) { $ret = array(); foreach ($query as $k => $v) { if (in_array($k, array("streetaddr", "city", "state", "zip", "lat", "lon", "location", "range"))) { $ret[$k] = $v; } } $url = $this->cfg["server"]; $url .= "?appid=" . urlencode($this->cfg["appid"]); $url .= "&output=php"; $urlargs = ""; $logaddr = ""; if (!empty($ret["streetaddr"])) { $urlextra .= "&street=" . urlencode($ret["streetaddr"]); $logaddr .= $ret["streetaddr"]; } if (!empty($ret["city"])) { $urlextra .= "&city=" . urlencode($ret["city"]); $logaddr .= (!empty($logaddr) ? ", " : "") . $ret["city"]; } if (!empty($ret["state"])) { $urlextra .= "&state=" . urlencode($ret["state"]); $logaddr .= (!empty($logaddr) ? ", " : "") . $ret["state"]; } if (!empty($ret["zip"])) { $urlextra .= "&zip=" . urlencode($ret["zip"]); $logaddr .= (!empty($logaddr) ? ", " : "") . $ret["zip"]; } if (!empty($ret["location"])) { $urlextra .= "&location=" . urlencode($ret["location"]); $logaddr .= (!empty($logaddr) ? ", " : "") . $ret["location"]; } if (!empty($urlextra) && ($response = file_get_contents($url . $urlextra))) { $logstr = "Geocoding address: {$logaddr}"; $responseobj = unserialize($response); if (!empty($responseobj["ResultSet"])) { $resultset = $responseobj["ResultSet"]; if (!empty($resultset["Result"])) { //$ret = array($resultset["Result"]["Latitude"], $resultset["Result"]["Longitude"]); if (empty($ret["city"]) && !empty($resultset["Result"]["City"])) { $ret["city"] = $resultset["Result"]["City"]; } if (empty($ret["state"]) && !empty($resultset["Result"]["State"])) { $ret["state"] = $resultset["Result"]["State"]; } if (empty($ret["zip"]) && !empty($resultset["Result"]["Zip"])) { $ret["zip"] = $resultset["Result"]["Zip"]; } $ret["lat"] = $resultset["Result"]["Latitude"]; $ret["lon"] = $resultset["Result"]["Longitude"]; $logstr .= " (" . $ret["lat"] . ", " . $ret["lon"] . ")"; } else { $logstr .= " (FAILED)"; } } Logger::Info($logstr); } return $ret; }
function ensureAdmin($f3) { $adminSecret = $this->getAdminSecret($f3); if ($f3->exists('COOKIE.admin_secret') && $f3->get('COOKIE.admin_secret') === $adminSecret) { $f3->set('isAdmin', true); return true; } else { $user = ""; if (isset($_SERVER['PHP_AUTH_USER'])) { $user = $_SERVER['PHP_AUTH_USER']; } $password = ""; if (isset($_SERVER['PHP_AUTH_PW'])) { $password = $_SERVER['PHP_AUTH_PW']; } $source = "unknown"; if (isset($_SERVER['REMOTE_ADDR'])) { $source = $_SERVER['REMOTE_ADDR']; } Logger::Info($f3, "HandlerBase.ensureAdmin", "Login attempt with user '{$user}', pass '{$password}' from '{$source}'"); if ($user == 'admin' && $password == $f3->get('settings.adminPassword')) { $f3->set('COOKIE.admin_secret', $adminSecret); $f3->set('isAdmin', true); return true; } else { Logger::Warn($f3, "HandlerBase.ensureAdmin", "Root access denied to IP {$_SERVER['REMOTE_ADDR']}"); header('WWW-Authenticate: Basic realm="Elternsprechtag St. Rupert"'); header('HTTP/1.0 401 Unauthorized'); echo "You must enter a valid login ID and password to access this resource\n"; $f3->set('isAdmin', false); return false; } } }
function logout($f3) { if ($this->isLoggedIn($f3)) { $userId = $f3->get('COOKIE.user_id'); Logger::Info($f3, "UserGet.logout", "User: {$userId}"); $f3->clear('COOKIE.user_id'); } $f3->reroute('/login'); }
function lock($f3) { if ($f3->exists('GET.newLockStatus')) { try { $db = $f3->get('db'); $newLockStatus = $f3->get('GET.newLockStatus'); Logger::Info($f3, "AdminGet.lock", "Setting lock setting to {$newLockStatus}"); DbWrapper::updateEinstellung($db, 'isLocked', $newLockStatus); } catch (Exception $e) { Logger::Error($f3, "AdminGet.lock", "Error: {$e->getMessage()}"); } } $f3->reroute('/admin/index'); }
function release($f3, $params) { $lehrerId = $params['id']; if ($f3->exists('POST.zeitId')) { try { $zeitId = $f3->get('POST.zeitId'); $userId = $f3->get('COOKIE.user_id'); Logger::Info($f3, "LehrerPost.release", "Lehrer: {$lehrerId}, Zeit: {$zeitId}, User: {$userId}"); DbWrapper::deleteReservation($f3->get('db'), $lehrerId, $zeitId, $userId); } catch (Exception $e) { Logger::Error($f3, "LehrerPost.release", "Lehrer: {$lehrerId}, Error: {$e->getMessage()}"); } } $f3->reroute('/lehrer/' . $lehrerId); }
function login($f3) { if ($f3->exists('POST.schueler_name')) { $schuelerName = $f3->get('POST.schueler_name'); try { $schuelerId = DbWrapper::getSchuelerIdByName($f3->get('db'), $schuelerName); Logger::Info($f3, "UserPost.login", "Name: {$schuelerName}, Id: {$schuelerId}"); $f3->set('COOKIE.user_id', $schuelerId); $f3->set('COOKIE.user_name', $schuelerName); $f3->reroute('/'); } catch (Exception $e) { Logger::Error($f3, "UserPost.login", "Name: {$schuelerName}, Error: {$e->getMessage()}"); $f3->reroute('/login'); } } else { $f3->reroute('/login'); } }
function Init(&$cfg) { $this->cfg =& $cfg; Logger::Info("DataManager initializing"); Profiler::StartTimer("DataManager::Init()", 1); //Profiler::StartTimer("DataManager::Init() - caches"); if (!empty($this->cfg->servers["caches"])) { foreach ($this->cfg->servers["caches"] as $cachename => $cachecfg) { $this->AddCaches($cachename, $cachecfg); } } //Profiler::StopTimer("DataManager::Init() - caches"); //Profiler::StartTimer("DataManager::Init() - sources"); if (!empty($this->cfg->servers["sources"])) { foreach ($this->cfg->servers["sources"] as $sourcename => $sourcecfg) { $this->AddSource($sourcename, $sourcecfg); } } //Profiler::StopTimer("DataManager::Init() - sources"); //include_once("config/outlet-conf.php"); //$this->outlet = Outlet::getInstance(); //print_pre($this->sources); Profiler::StopTimer("DataManager::Init()"); }
function Quote($queryid, $str) { $servers = $this->HashToServer($queryid); foreach ($servers as $server) { $servernum = $server[0]; if (!$this->LazyOpen($servernum)) { Logger::Info("Database connection '{$this->name}:{$servernum}' marked as failed, can't quote"); } else { return $this->conn[$servernum]->quote($str); } } return mysql_escape_string($str); // Deprecated function call used as a last-ditch fallback }
/** * Returns the memcache key value. * * @param string $key * @return mixed */ protected function getData($key) { $diskcache = $this->getCachePaths($key); $ret = false; if (file_exists($diskcache["fullpath"])) { if (filemtime($diskcache["fullpath"]) < $this->lifetime) { Logger::Warn("Unlinking stale cachefile '" . $diskcache["fullpath"] . "'"); unlink($diskcache["fullpath"]); } else { $fp = fopen($diskcache["fullpath"], "r"); if (flock($fp, LOCK_SH)) { //$cachedresult = file_get_contents($diskcache["fullpath"]); $cachedresult = fread($fp, filesize($diskcache["fullpath"])); flock($fp, LOCK_UN); if (!empty($cachedresult)) { Profiler::StartTimer("DataManager::Query() - load from cachefile"); Logger::Info("Loaded cached result for '%s' from file: '%s'", $key, $diskcache["fullpath"]); $result = unserialize(gzdecode($cachedresult)); if ($result !== false) { $ret = $result; } Profiler::StopTimer("DataManager::Query() - load from cachefile"); } } else { Logger::Error("Failed to get shared lock for '%s' (%s)", $key, $diskcache["fullpath"]); } } } return $ret; }
function ApplyConfigOverrides() { if (!empty($this->request["args"]["sitecfg"])) { $tmpcfg = array(); array_set_multi($tmpcfg, $this->request["args"]["sitecfg"]); // FIXME - can't we just array_set_multi() on $this->sitecfg directly? ConfigManager::merge($tmpcfg); } if (!empty($this->request["args"]["cobrandoverride"])) { $included_config =& $this->cfg->GetConfig($this->request["args"]["cobrandoverride"], false, $this->cfg->role); if (!empty($included_config)) { ConfigManager::merge($included_config); } } $rolename = any($this->request["args"]["_role"], $this->cfg->role); $rolecfg = ConfigManager::get("roles.{$rolename}.options"); if (!empty($rolecfg)) { Logger::Info("Using overridden role cfg 'roles.{$rolename}'"); ConfigManager::merge($rolecfg); } if ($this->request["ssl"]) { $included_config = $this->cfg->GetConfig("classes.secure", false, $this->cfg->role); if (!empty($included_config)) { ConfigManager::merge($included_config); } } $browseroverride = any($this->request["args"]["sess"]["browser.override"], $_SESSION["temporary"]["user"]["preferences"]["browser"]["override"], NULL); if ($browseroverride !== NULL) { $this->request["browser"] = $browseroverride; } if (!empty($this->request["browser"])) { $included_config =& ConfigManager::get("browsers.{$this->request['browser']}.options"); if (!empty($included_config["include"])) { // These includes sure do get hairy. This allows for browsers.*.options.include to call in different classes $includes = explode(",", $included_config["include"]); foreach ($includes as $include) { $subincluded_config =& $this->cfg->GetConfig($include, false, $this->cfg->role); if (!empty($subincluded_config)) { ConfigManager::merge($subincluded_config); } } unset($included_config["include"]); } if (!empty($included_config)) { ConfigManager::merge($included_config); } } if ($this->debug) { $subincluded_config =& $this->cfg->GetConfig("classes.debug", false, $this->cfg->role); if (!empty($subincluded_config)) { ConfigManager::merge($subincluded_config); } } }
function ApplyRedirects($req, $rules) { $doRedirect = false; foreach ($rules as $rule) { //if (!empty($rule->match)) { // FIXME - Never ever upgrade to PHP 5.2.6. It breaks empty() on SimpleXML objects. if ($rule->match) { $ismatch = true; $isexcept = false; $matchvars = array(NULL); // Force first element to NULL to start array indexing at 1 (regex-style) foreach ($rule->match->attributes() as $matchkey => $matchstr) { $checkstr = array_get($req, $matchkey); if ($checkstr !== NULL) { $m = NULL; if (substr($matchstr, 0, 1) == "!") { $ismatch &= !preg_match("#" . substr($matchstr, 1) . "#", $checkstr, $m); } else { $ismatch &= preg_match("#" . $matchstr . "#", $checkstr, $m); } //Logger::Debug("Check rewrite (%s): '%s' =~ '%s' ? %s", $matchkey, $checkstr, $matchstr, ($ismatch ? "YES" : "NO")); if (is_array($m) && count($m) > 0) { if (count($m) > 1) { for ($i = 1; $i < count($m); $i++) { $matchvars[] = $m[$i]; } } } } else { if (substr($matchstr, 0, 1) != "!") { $ismatch = false; } } } if ($ismatch && $rule->except) { $exceptflag = true; foreach ($rule->except->attributes() as $exceptkey => $exceptstr) { $checkstr = array_get($req, $exceptkey); if ($checkstr !== NULL) { $m = NULL; if (substr($exceptstr, 0, 1) == "!") { $exceptflag &= !preg_match("#" . substr($exceptstr, 1) . "#", $checkstr, $m); } else { $exceptflag &= preg_match("#" . $exceptstr . "#", $checkstr, $m); } } } if ($exceptflag) { $isexcept = true; } } if ($ismatch && !$isexcept) { // Apply nested rules first... if ($rule->rule) { $req = $this->ApplyRedirects($req, $rule->rule); } // Then process "set" command if ($rule->set) { Logger::Info("Applying redirect:\n " . $rule->asXML()); if (!empty($req["args"]["testredir"])) { print "<pre>" . htmlspecialchars($rule->asXML()) . "</pre><hr />"; } foreach ($rule->set->attributes() as $rewritekey => $rewritestr) { if (count($matchvars) > 1 && strpos($rewritestr, "%") !== false) { $find = array(NULL); for ($i = 1; $i < count($matchvars); $i++) { $find[] = "%{$i}"; } $rewritestr = str_replace($find, $matchvars, $rewritestr); } array_set($req, (string) $rewritekey, (string) $rewritestr); } if ($rule["type"] == "redirect") { $doRedirect = 301; } else { if ($rule["type"] == "bounce") { $doRedirect = 302; } } } // And finally process "unset" if ($rule->unset) { $unset = false; foreach ($rule->unset->attributes() as $unsetkey => $unsetval) { if ($unsetkey == "_ALL_" && $unsetval == "ALL") { $req["args"] = array(); } else { if (!empty($unsetval)) { $reqval = array_get($req, $unsetkey); if ($reqval !== NULL) { array_unset($req, $unsetkey); $unset = true; } } } } if ($unset) { if ($rule["type"] == "redirect") { $doRedirect = 301; } else { if ($rule["type"] == "bounce") { $doRedirect = 302; } } } } if ($doRedirect !== false) { break; } } } } if ($doRedirect !== false) { $origscheme = "http" . ($req["ssl"] ? "s" : ""); if ($req["host"] != $_SERVER["HTTP_HOST"] || $req["scheme"] != $origscheme) { $newurl = sprintf("%s://%s%s", $req["scheme"], $req["host"], $req["path"]); } else { $newurl = $req["path"]; } if (empty($req["args"]["testredir"])) { if (empty($req["friendly"])) { $querystr = makeQueryString($req["args"]); $newurl = http_build_url($newurl, array("query" => $querystr)); } else { $newurl = makeFriendlyURL($newurl, $req["args"]); } if ($newurl != $req["url"]) { http_redirect($newurl, NULL, true, $doRedirect); } } else { print_pre($req); } } return $req; }
/** * This abstract class will be implemented by the * child class. It connects the list of memcache * servers added. */ protected function connectServers() { // if no servers, error out if (empty($this->servers)) { throw new Exception('No memcache servers to connect to.'); } foreach ($this->servers as $host => $params) { Logger::Info("Added memcache server - %s:%s (persist=%d)", $host, $params["port"], $params["persist"]); $this->cache_obj->addServer($host, $params["port"], $params["persist"]); } }
public function loadFromCache() { $cachekey = "config.{$this->role}.{$this->name}"; $data = DataManager::singleton(); $cachewrapper =& $data->caches["apc"]["default"]; if (($cachedresult = $cachewrapper->get($cachekey)) !== false) { $configobj = unserialize($cachedresult); Logger::Info("Found '{$cachekey}' in apc cache (revision=" . $cacheobj->revision . ")"); $allversions = $this->GetAllRevisions($role); Logger::Debug($allversions); foreach ($cacheobj->heirarchy as $i => $inc) { if ($cacheobj->versions[$inc] < $allversions[$inc]) { Logger::Warn("Revision number for '{$name}' parent '{$inc}' is out of date (old revision=" . $cacheobj->versions[$inc] . " new revision=" . $allversions[$inc] . ")"); $ret = array(); $skipcache = true; for ($j = $i; $j >= 0; $j--) { // Clear cache entries for anything above this one Logger::Info("Delete cache: {$includes[$j]}"); /* // FIXME - need to invalidate instead of deleting $cachewrapper->delete("config.$role.{$includes[$j]}"); $cachewrapper->delete("config.$role.{$includes[$j]}.heirarchy"); $cachewrapper->delete("config.$role.{$includes[$j]}.versions"); */ } } } } }
/** * mysql session write handler. * Persist the entire portion of $_SESSION["persist"] to DB. * Refresh memcache with $_SESSION. * * @param string session id * @param string data to write */ public function write($id, $data) { Profiler::StartTimer("SessionManager::write", 1); // Save user, and all associated lists, settings, etc. Profiler::StartTimer("SessionManager::write - save user"); $user = User::singleton(); $user->save(); Profiler::StopTimer("SessionManager::write - save user"); $pdata = $_SESSION["persist"]; strip_nulls($pdata); // compare the persist data before and after for changes $pdata_serialize = serialize($pdata); //Logger::Warn($pdata_serialize); //$data = $this->cache_obj->get($id); $data = DataManager::fetch("memcache.session#{$id}", $id); $pdata_before = $data["persist"]; $pdata_before_serialize = serialize($pdata_before); // update the memcache with the entire $_SESSION //$session_serialize = serialize($_SESSION); //$this->cache_obj->set($id, $_SESSION, 0); $data = DataManager::update("memcache.session#{$id}", $id, $_SESSION); if (empty($pdata) || is_null($pdata)) { $new_crc = 0; } else { $new_crc = strlen($pdata_serialize) . crc32($pdata_serialize); } if ($this->crc != $new_crc) { // if the user does not have a record already, create one first // NOTE from James - removed '&& (! $pdata["user"]["userid"]' from below, didn't seem to make sense... if ($this->has_db_record == false) { $this->create_new_persist_record(); // need to set the session cache again with we set the has_db_record param //$session_serialize = serialize($_SESSION); //$this->cache_obj->set($id, $_SESSION, 0, $this->session_cache_expire); DataManager::update("memcache.session#{$id}", $id, $_SESSION); } else { $result = $this->data->QueryUpdate($this->sessionsource . "#{$this->fluid}", $this->sessiontable, array($this->fluid => array("data" => $pdata_serialize)), array("fl_uid" => $this->fluid)); Logger::Info("Updating userdata.usersession record for {$this->fluid}"); } } Profiler::StopTimer("SessionManager::write"); }
function ApplySingleOverride(&$final, $param_key, $param_value = NULL, $param_args = NULL, $applysettings = true) { $cfg = ConfigManager::singleton(); $logmsg = "Applying URL map for {$param_key} ({$param_value})"; if (!empty($param_args["override"])) { $cfg->ConfigMerge($cfg->current, $param_args["override"]); $param_args = ConfigManager::get("search.request.override.{$param_key}"); } $new_value = NULL; if (!empty($param_args["values"][$param_value])) { $valuemap = $param_args["values"][$param_value]; if (is_array($valuemap)) { if (!empty($valuemap["override"])) { $cfg->ConfigMerge($cfg->current, $valuemap["override"]); $param_args = ConfigManager::get("search.request.override.{$param_key}"); } if (isset($valuemap["newvalue"])) { $new_value = $valuemap["newvalue"]; $logmsg .= " (newvalue: '{$new_value}')"; } else { $new_value = $param_value; } } else { $new_value = $param_args["values"][$param_value]; } } else { $new_value = $param_value; } if (!empty($param_args["key"]) && $new_value !== NULL) { array_set($final, $param_args["key"], $new_value); //array_unset($final,$param_key); } if (!empty($param_args["alsooverride"])) { $alsolog = ""; //print_pre($param_values["alsooverride"]); foreach ($param_args["alsooverride"] as $also_key => $also_value) { $also_params = ConfigManager::get("search.request.override.{$also_key}"); if (!empty($also_params)) { $alsolog .= (!empty($alsolog) ? "; " : "") . "{$also_key}={$also_value}"; $this->ApplySingleOverride($final, $also_key, $also_value, $also_params, $applyoverrides); } } if (!empty($alsolog)) { $logmsg .= "(ALSO: {$alsolog})"; } } if ($applysettings && !empty($param_args["settings"])) { $settingslog = ""; $updated_settings = ConfigManager::get("search.request.override.{$param_key}.settings"); if (!empty($updated_settings)) { $flattened_settings = array_flatten($updated_settings); $user = User::singleton(); foreach ($flattened_settings as $setting_key => $setting_value) { $settingslog .= (!empty($settingslog) ? "; " : "") . "{$setting_key}={$setting_value}"; $user->SetPreference($setting_key, $setting_value, "temporary"); } } if (!empty($settingslog)) { $logmsg .= " (SETTINGS: {$settingslog})"; } } Logger::Info($logmsg); }
function changePassword($f3, $params) { $this->ensureAdmin($f3); $newPassword = $f3->get('POST.password'); Logger::Info($f3, "AdminPost.changePassword", "Changing the admin password"); DbWrapper::updateEinstellung($f3->get('db'), 'adminPassword', $newPassword); $f3->reroute('/admin/index'); }
function isExpired() { if ($this->soft) { $now = time(); $ret = $this->timestamp + $this->timeout < $now; //print_pre("isExpired ? " . sprintf("%d + %d < %d (%d)", $this->timestamp, $this->timeout, $now, $ret)); //$this->getProbability(20); if ($ret) { Logger::Info("Soft-expiring '{$this->id}'"); } } else { $ret = $this->payload === NULL; //print_pre("isExpired ? $ret"); } return $ret; }