/** * (non-PHPdoc) * * @see \vxPHP\SimpleTemplate\Filter\SimpleTemplateFilterInterface::parse() * */ public function apply(&$templateString) { $application = Application::getInstance(); $assetsPath = '/' . ($application->hasNiceUris() ? '' : $application->getRelativeAssetsPath()); // extend <img src="$..." ...> with path to site images $templateString = preg_replace_callback('~<img(.*?)\\s+src=("|\')\\$([^"\']+)\\2(.*?)>~i', function ($matches) use($assetsPath) { return '<img' . $matches[1] . ' src=' . $matches[2] . $assetsPath . 'img/site/' . $matches[3] . $matches[2] . $matches[4] . '>'; }, $templateString); // change path of src and href attributes when asset_path is set and use_nice_uris is not set in the configuration // only relative links (without protocol) are matched // when nice uris are used URL rewriting does the job, when no assets path is set, everything is in place already if ($application->getRelativeAssetsPath() && !$application->hasNiceUris()) { // src attributes $templateString = preg_replace_callback('~<(.*?)\\s+src=("|\')(?![a-z]+://)([^"\']+)\\2(.*?)>~i', function ($matches) use($assetsPath) { return '<' . $matches[1] . ' src=' . $matches[2] . $assetsPath . ltrim($matches[3], '/') . $matches[2] . $matches[4] . '>'; }, $templateString); // href attributes $templateString = preg_replace_callback('~<(.*?)\\s+href=("|\')(?![a-z]+://)([^"\']+)\\2(.*?)>~i', function ($matches) use($assetsPath) { // check whether this URL has already the assets path prefixed and contains a script - in this case don't change the URL if (preg_match('~^' . $assetsPath . '\\w+\\.php~', $matches[3])) { return $matches[0]; } return '<' . $matches[1] . ' href=' . $matches[2] . $assetsPath . ltrim($matches[3], '/') . $matches[2] . $matches[4] . '>'; }, $templateString); } }
private static function queryAllNotifications() { $rows = Application::getInstance()->getDb()->doPreparedQuery("\n\t\t\tSELECT\n\t\t\t\tnotificationsID as id,\n\t\t\t\tn.Alias,\n\t\t\t\tIFNULL(Description, n.Alias) AS Description,\n\t\t\t\tSubject,\n\t\t\t\tMessage,\n\t\t\t\tSignature,\n\t\t\t\tAttachment,\n\t\t\t\tNot_Displayed,\n\t\t\t\tag.Alias as group_alias\n\n\t\t\tFROM\n\t\t\t\tnotifications n\n\t\t\t\tINNER JOIN admingroups ag ON ag.admingroupsID = n.admingroupsID\n\t\t"); self::$cachedNotificationData = array(); foreach ($rows as $r) { $r['Attachment'] = preg_split('~\\s*,\\s*~', $r['Attachment']); self::$cachedNotificationData[$r['Alias']] = $r; } }
/** * (non-PHPdoc) * * @see \vxPHP\SimpleTemplate\Filter\SimpleTemplateFilterInterface::parse() * */ public function apply(&$templateString) { $application = Application::getInstance(); $config = $application->getConfig(); $locale = $application->getCurrentLocale(); // without locale replace placeholders with their identifier if (empty($locale)) { $templateString = preg_replace(array('@\\{![a-z0-9_]+\\}@i', '@\\{![a-z0-9_]+:(.*?)\\}@i'), array('', '$1'), $templateString); return; } $this->getPhrases($locale); $templateString = preg_replace_callback('@\\{!([a-z0-9_]+)(:(.*?))?\\}@i', array($this, 'translatePhraseCallback'), $templateString); }
/** * constructor, parses options * * @param array $options * @throws \InvalidArgumentException */ public function __construct(array $options = []) { $this->locale = isset($options['locale']) ? $options['locale'] : Application::getInstance()->getCurrentLocale(); if (!$this->locale instanceof Locale) { throw new \InvalidArgumentException("Date validator option 'locale' is not a Locale instance."); } if (isset($options['validFrom'])) { if (!$options['validFrom'] instanceof \DateTime) { throw new \InvalidArgumentException("Date validator option 'validFrom' is not a DateTime instance."); } $this->validFrom = $options['validFrom']; } if (isset($options['validUntil'])) { if (!$options['validUntil'] instanceof \DateTime) { throw new \InvalidArgumentException("Date validator option 'validUntil' is not a DateTime instance."); } $this->validUntil = $options['validUntil']; } if (is_null($this->locale)) { throw new \InvalidArgumentException('Date validator requires either a valid locale, either passed to constructor or configured in application.'); } }
/** * create anchor tag * * @param string link URI or relative link * @param string text link test * @param string img image name used within link * @param string class css class * @param string miscstr additional string with attributes or handlers * @param string $counted add code for counting clicks on link */ public static function a($link, $text = '', $img = '', $class = FALSE, $miscstr = FALSE) { if (empty($link)) { return FALSE; } $mail = self::checkMail($link); $ext = !$mail ? self::checkExternal($link) : TRUE; if ($mail) { $enc = 'mailto:'; $len = strlen($link); for ($i = 0; $i < $len; $i++) { $enc .= rand(0, 1) ? '&#x' . dechex(ord($link[$i])) . ';' : '&#' . ord($link[$i]) . ';'; } $link = $enc; } else { if (!$ext && Application::getInstance()->hasNiceUris()) { $link = NiceURI::toNice($link); } $link = htmlspecialchars($link); } $text = $text == '' && $img == '' ? preg_replace('~^\\s*[a-z]+:(//)?~i', '', $link) : $text; $class = $class ? array($class) : array(); if (self::checkExternal($link)) { $class[] = 'external'; } $html = array('<a', !empty($class) ? ' class="' . implode(' ', $class) . '"' : '', " href='{$link}'", $miscstr ? " {$miscstr}>" : '>', $img != '' ? "<img src='{$img}' alt='{$text}'>" : $text, '</a>'); return implode('', $html); }
/** * retrieve all available categories sorted by $sortCallback * * @param mixed $sortCallback * @throws ArticleCategoryException * @return array categories */ public static function getArticleCategories($sortCallback = NULL) { $cat = array(); foreach (Application::getInstance()->getDb()->doPreparedQuery('SELECT articlecategoriesID FROM articlecategories') as $r) { $cat[] = self::getInstance($r['articlecategoriesID']); } if (is_null($sortCallback)) { return $cat; } if (is_callable($sortCallback)) { usort($cat, $sortCallback); return $cat; } if (is_callable("self::{$sortCallback}")) { usort($cat, "self::{$sortCallback}"); return $cat; } throw new ArticleCategoryException("'{$sortCallback}' is not callable.", ArticleCategoryException::ARTICLECATEGORY_SORT_CALLBACK_NOT_CALLABLE); }
/** * create response * with a HttpException status code and headers are considered * other exceptions default to status code 500 * a error_docs/error_{status_code}.php template is parsed, when found * otherwise the exception data is decorated and dumped * * @param \Exception $e * @return \vxPHP\Http\Response */ protected function createResponse(\Exception $e) { if ($e instanceof HttpException) { $status = $e->getStatusCode(); $headers = $e->getHeaders(); } else { $status = Response::HTTP_INTERNAL_SERVER_ERROR; $headers = array(); } $config = Application::getInstance()->getConfig(); if (isset($config->paths['tpl_path'])) { $path = ($config->paths['tpl_path']['absolute'] ? '' : rtrim(Application::getInstance()->getRootPath(), DIRECTORY_SEPARATOR)) . $config->paths['tpl_path']['subdir']; if (file_exists($path . 'error_docs' . DIRECTORY_SEPARATOR . 'error_' . $status . '.php')) { $tpl = SimpleTemplate::create('error_docs' . DIRECTORY_SEPARATOR . 'error_' . $status . '.php'); $content = $tpl->assign('exception', $e)->assign('status', $status)->display(); } else { $content = $this->decorateException($e, $status); } } else { $content = $this->decorateException($e, $status); } return new Response($content, $status, $headers); }
/** * retrieve cached image which matches src attribute $src and actions $actions * if no cached image is found, a cached image with $actions applied is created * * @param string $src * @param string $actions * @throws SimpleTemplateException * @return string */ private function getCachedImagePath($src, $actions) { $pathinfo = pathinfo($src); $extension = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : ''; // destination file name $dest = $pathinfo['dirname'] . '/' . FilesystemFolder::CACHE_PATH . '/' . $pathinfo['filename'] . $extension . '@' . $actions . $extension; // absolute path to cached file $path = Application::getInstance()->extendToAbsoluteAssetsPath(ltrim($dest, '/')); // generate cache directory and file if necessary if (!file_exists($path)) { $cachePath = dirname($path); if (!file_exists($cachePath)) { if (!@mkdir($cachePath)) { throw new SimpleTemplateException("Failed to create cache folder {$cachePath}"); } chmod($cachePath, 0777); } // apply actions and create file $actions = explode('|', $actions); $imgEdit = ImageModifierFactory::create(Application::getInstance()->extendToAbsoluteAssetsPath(ltrim($pathinfo['dirname'], '/') . '/' . $pathinfo['basename'])); foreach ($actions as $a) { $params = preg_split('~\\s+~', $a); $method = array_shift($params); if (method_exists($imgEdit, $method)) { call_user_func_array(array($imgEdit, $method), $params); } } $imgEdit->export($path); } return $dest; }
/** * stores allowed notification for user in database * * @param array $notification_aliases */ public function setNotifications(array $aliases) { $db = Application::getInstance()->getDb(); if (is_null($this->adminid)) { return; } $db->execute('DELETE FROM admin_notifications WHERE adminID = ?', array($this->adminid)); $this->cachedNotifications = NULL; $available = Notification::getAvailableNotifications($this->group_alias); $ids = array(); foreach ($aliases as $a) { if (isset($available[$a])) { $ids[] = $available[$a]->id; } } if (!empty($ids)) { foreach ($ids as $i) { $db->execute("INSERT INTO admin_notifications (adminID, notificationsID) VALUES(?, ?)", array($this->adminid, $i)); } $this->getNotifications(); } }
/** * evaluate mailer class and send mail * * @return boolean */ private function sendMail() { // check for configured mailer if (is_null($this->mailer) && !is_null(Application::getInstance()->getConfig()->mail->mailer)) { $mailer = Application::getInstance()->getConfig()->mail->mailer; $reflection = new \ReflectionClass(str_replace('/', '\\', $mailer->class)); $port = isset($mailer->port) ? $mailer->port : NULL; $encryption = isset($mailer->encryption) ? $mailer->encryption : NULL; $this->mailer = $reflection->newInstanceArgs(array($mailer->host, $port, $encryption)); if (isset($mailer->auth_type)) { $this->mailer->setCredentials($mailer->user, $mailer->pass, $mailer->auth_type); } else { $this->mailer->setCredentials($mailer->user, $mailer->pass); } } if (is_null($this->mailer)) { // use PHP's own mail() function $headers = array(); foreach ($this->headers as $k => $v) { $headers[] = iconv_mime_encode($k, $v); } mb_internal_encoding($this->encoding); // @todo ensure receiver to be RFC conforming return mail(implode(',', $this->receiver), mb_encode_mimeheader($this->subject, mb_internal_encoding(), 'Q'), $this->msg, implode(self::CRLF, $headers)); } else { // send mail with configured mailer try { $this->mailer->connect(); $this->mailer->setFrom($this->sender); $this->mailer->setTo(array_merge((array) $this->receiver, $this->cc, $this->bcc)); $this->mailer->setHeaders(array_merge(array('To' => implode(',', (array) $this->receiver), 'Subject' => $this->subject), $this->headers)); $this->mailer->setMessage($this->msg); $this->mailer->send(); $this->mailer->close(); return TRUE; } catch (MailerException $e) { $this->mailer->close(); return $e->getMessage(); } } }
/** * initialize menu renderer * * @param Menu $menu */ public function __construct(Menu $menu) { $this->menu = $menu; $this->hasNiceUris = Application::getInstance()->hasNiceUris(); }
/** * (non-PHPdoc) * * @see \vxPHP\SimpleTemplate\Filter\SimpleTemplateFilterInterface::parse() * */ public function apply(&$templateString) { $this->encoding = strtoupper(Application::getInstance()->getConfig()->site->default_encoding); $templateString = preg_replace_callback('~(^|\\s|>|(?:<a [^>]*?>.*?))' . Rex::URI_STRICT . '(<|\\s|$)~i', array($this, 'urlAnchors'), $templateString); $templateString = preg_replace_callback('~(<a [^>]*?>.*?|)(' . Rex::EMAIL . ')([^<]*</a>|)~i', array($this, 'obfuscatedMailAnchors'), $templateString); }
/** * get href attribute value of menu entry */ public function getHref() { if (is_null($this->href)) { if ($this->localPage) { $pathSegments = []; $e = $this; do { $pathSegments[] = $e->path; } while ($e = $e->menu->getParentEntry()); if (Application::getInstance()->hasNiceUris()) { if (($script = basename($this->menu->getScript(), '.php')) == 'index') { $script = '/'; } else { $script = '/' . $script . '/'; } } else { $script = '/' . $this->menu->getScript() . '/'; } $this->href = $script . implode('/', array_reverse($pathSegments)); } else { $this->href = $this->path; } } return $this->href; }
/** * * @param string $scriptName (e.g. index.php, admin.php) * @param array $pathSegments * * @return \vxPHP\Routing\Route */ private static function getRouteFromConfig($scriptName, array $pathSegments = NULL) { $routes = Application::getInstance()->getConfig()->routes; // if no page given try to get the first from list if (is_null($pathSegments) && isset($routes[$scriptName])) { return array_shift($routes[$scriptName]); } $pathToCheck = implode('/', $pathSegments); $requestMethod = Request::createFromGlobals()->getMethod(); $foundRoute = NULL; $default = NULL; // iterate over routes and try to find the "best" match foreach ($routes[$scriptName] as $route) { // keep default route as fallback, when no match is found if ($route->getRouteId() === 'default') { $default = $route; } // pick route only when request method requirement is met if (preg_match('~(?:/|^)' . $route->getMatchExpression() . '(?:/|$)~', $pathToCheck) && $route->allowsRequestMethod($requestMethod)) { // if no route was found yet, pick this first match if (!isset($foundRoute)) { $foundRoute = $route; } else { // if a route has been found previously, choose the more "precise" and/or later one // choose the route with more satisfied placeholders // @todo could be optimized if (count(self::getSatisfiedPlaceholders($route, $pathToCheck)) >= count(self::getSatisfiedPlaceholders($foundRoute, $pathToCheck))) { $foundRoute = $route; } } } } // return "normal" route, if found if (isset($foundRoute)) { return $foundRoute; } // return default route as fallback (if available) return $default; }
/** * determines controller class name from a routes controllerString property * and returns a controller instance * * @param Route $controllerPath * @return Controller */ public static function createControllerFromRoute(Route $route) { $controllerClass = Application::getInstance()->getApplicationNamespace() . $route->getControllerClassName(); /** * @var Controller */ $instance = new $controllerClass(); if ($method = $instance->route->getMethodName()) { $instance->setExecutedMethod($method); } else { $instance->setExecutedMethod('execute'); } return $instance; }
/** * creates metafolder from supplied filesystem folder * nested set is updated accordingly * * @param FilesystemFolder $f * @param array $metaData optional data for folder * @throws MetaFolderException */ public static function create(FilesystemFolder $f, array $metaData = []) { if ($parentFolder = $f->getParentFolder()) { $parentFolder->createMetaFolder(); } try { self::getInstance($f->getPath()); } catch (MetaFolderException $e) { $db = Application::getInstance()->getDb(); if (strpos($f->getPath(), Application::getInstance()->getAbsoluteAssetsPath()) === 0) { $metaData['Path'] = trim(substr($f->getPath(), strlen(Application::getInstance()->getAbsoluteAssetsPath())), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; } else { $metaData['Path'] = rtrim($f->getPath(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; } $metaData['Alias'] = strtolower(preg_replace('~[\\\\/]~', '_', rtrim($metaData['Path'], DIRECTORY_SEPARATOR))); if (!isset($metaData['Access']) || !preg_match('~^rw?$~i', $metaData['Access'])) { $metaData['Access'] = 'RW'; } else { $metaData['Access'] = strtoupper($metaData['Access']); } $tree = explode(DIRECTORY_SEPARATOR, trim($metaData['Path'], DIRECTORY_SEPARATOR)); if (count($tree) == 1) { //no parent $rows = $db->doPreparedQuery('SELECT MAX(r) + 1 AS l FROM folders'); $metaData['l'] = !isset($rows[0]['l']) ? 0 : $rows[0]['l']; $metaData['r'] = $rows[0]['l'] + 1; $metaData['level'] = 0; } else { array_pop($tree); try { $parent = self::getInstance(implode(DIRECTORY_SEPARATOR, $tree) . DIRECTORY_SEPARATOR); // has parent directory $rows = $db->doPreparedQuery("SELECT r, l, level FROM folders WHERE foldersID = ?", [$parent->getId()]); $db->execute('UPDATE folders SET r = r + 2 WHERE r >= ?', [(int) $rows[0]['r']]); $db->execute('UPDATE folders SET l = l + 2 WHERE l > ?', [(int) $rows[0]['r']]); $metaData['l'] = $rows[0]['r']; $metaData['r'] = $rows[0]['r'] + 1; $metaData['level'] = $rows[0]['level'] + 1; } catch (MetaFolderException $e) { // no parent directory $rows = $db->doPreparedQuery('SELECT MAX(r) + 1 AS l FROM folders'); $metaData['l'] = !isset($rows[0]['l']) ? 0 : $rows[0]['l']; $metaData['r'] = $rows[0]['l'] + 1; $metaData['level'] = 0; } } $id = $db->insertRecord('folders', $metaData); // refresh nesting for all active metafolder instances foreach (array_keys(self::$instancesById) as $id) { self::getInstance(NULL, $id)->refreshNesting(); } return self::getInstance($f->getPath()); } throw new MetaFolderException('Metafolder for ' . $f->getPath() . ' already exists.', MetaFolderException::METAFOLDER_ALREADY_EXISTS); }
/** * callback to turn href shortcuts into site conform valid URLs * * $/foo/bar?baz=1 becomes /foo/bar?baz=1 or index.php/foo/bar?baz=1 * * @param array $matches * @return string */ private function filterHref($matches) { static $script; static $niceUri; if (empty($script)) { $script = trim(Request::createFromGlobals()->getScriptName(), '/'); } if (empty($niceUri)) { $niceUri = Application::getInstance()->getConfig()->site->use_nice_uris == 1; } $matches[4] = html_entity_decode($matches[4]); $uriParts = array(); if ($niceUri) { if ($script !== 'index.php') { $uriParts[] = basename($script, '.php'); } } else { $uriParts[] = $script; } if ($matches[3] !== '') { $uriParts[] = $matches[3]; } $uri = implode('/', $uriParts) . $matches[4]; return "<a{$matches[1]} href={$matches[2]}/{$uri}{$matches[2]}{$matches[5]}>"; }
/** * redirect using configured redirect information * if route has no redirect set, redirect will lead to "start page" * * @param array $queryParams * @param number $statusCode */ public function redirect($queryParams = [], $statusCode = 302) { $request = Request::createFromGlobals(); $application = Application::getInstance(); $urlSegments = [$request->getSchemeAndHttpHost()]; if ($application->hasNiceUris()) { if (($scriptName = basename($request->getScriptName(), '.php')) !== 'index') { $urlSegments[] = $scriptName; } } else { $urlSegments[] = trim($request->getScriptName(), '/'); } if (count($queryParams)) { $query = '?' . http_build_query($queryParams); } else { $query = ''; } return new RedirectResponse(implode('/', $urlSegments) . '/' . $this->redirect . $query); }
/** * returns path relative to assets path root * * @param boolean $force * @return string */ public function getRelativePath($force = FALSE) { if (!isset($this->relPath) || $force) { $relPath = preg_replace('~^' . preg_quote(Application::getInstance()->getAbsoluteAssetsPath(), '~') . '~', '', $this->path, -1, $replaced); $this->relPath = $replaced === 0 ? NULL : $relPath; } return $this->relPath; }
/** * get all articles assigned to given $category * * @param ArticleCategory $category * @return Array */ public static function getArticlesForCategory(ArticleCategory $category) { $articles = array(); $rows = Application::getInstance()->getDb()->doPreparedQuery('SELECT * FROM articles WHERE articlecategoriesID = ?', array($category->getId())); foreach ($rows as $r) { if (!isset(self::$instancesById[$r['articlesID']])) { // create Article instance if it does not yet exist $article = self::createInstance($r); self::$instancesByAlias[$article->alias] = $article; self::$instancesById[$article->id] = $article; } $articles[] = self::$instancesById[$r['articlesID']]; } return $articles; }
/** * converts plain to nice uris and vice-versa depending on configuration setting * * @param $uri * @return converted uri */ public static function autoConvert($uri) { $isPlain = self::isPlainURI($uri); if ($isPlain && Application::getInstance()->hasNiceUris()) { $uri = self::toNice($uri); } else { if (!$isPlain && !Application::getInstance()->hasNiceUris()) { $uri = self::toPlain($uri); } } return $uri; }
/** * walk the menu tree * and invoke service to append dynamic menu entries * * @param Menu $m */ protected function completeMenu(Menu $m) { if ($m->getType() === 'dynamic') { // invoke service to build menu entries Application::getInstance()->getService($m->getServiceId())->appendMenuEntries($m); } foreach ($m->getEntries() as $entry) { if ($m = $entry->getSubMenu()) { $this->completeMenu($m); } } }
/** * commit changes to metadata by writing data to database */ private function commit() { try { Application::getInstance()->getDb()->updateRecord('files', $this->id, $this->data); } catch (\PDOException $e) { throw new MetaFileException("Data commit of file '" . $this->filesystemFile->getFilename() . "' failed. PDO reports " . $e->getMessage()); } }
/** * creates a meta file based on filesystem file * @return MetaFile * @throws FilesystemFileException */ public function createMetaFile() { $db = Application::getInstance()->getDb(); if (count($db->doPreparedQuery("\n\t\t\tSELECT\n\t\t\t\tf.filesID\n\t\t\tFROM\n\t\t\t\tfiles f\n\t\t\t\tINNER JOIN folders fo ON fo.foldersID = f.foldersID\n\t\t\tWHERE\n\t\t\t\tf.File COLLATE utf8_bin = ? AND\n\t\t\t\tfo.Path COLLATE utf8_bin = ?\n\t\t\tLIMIT 1", array($this->filename, $this->folder->getRelativePath())))) { throw new FilesystemFileException("Metafile '{$this->filename}' in '{$this->folder->getRelativePath()}' already exists.", FilesystemFileException::METAFILE_ALREADY_EXISTS); } $mf = $this->folder->createMetaFolder(); $user = User::getSessionUser(); if (!($filesID = $db->insertRecord('files', array('foldersID' => $mf->getId(), 'File' => $this->filename, 'Mimetype' => $this->getMimetype(), 'createdBy' => is_null($user) ? NULL : $user->getAdminId())))) { throw new FilesystemFileException("Could not create metafile for '{$this->filename}'.", FilesystemFileException::METAFILE_CREATION_FAILED); } else { $mf = MetaFile::getInstance(NULL, $filesID); FileEvent::create(FileEvent::AFTER_METAFILE_CREATE, $this)->trigger(); return $mf; } }
/** * load template * * @return $success * @throws HtmlFormException */ private function loadTemplate() { if (!empty($this->template)) { return TRUE; } $path = Application::getInstance()->getRootPath() . (defined('FORMTEMPLATES_PATH') ? str_replace('/', DIRECTORY_SEPARATOR, ltrim(FORMTEMPLATES_PATH, '/')) : ''); if (!file_exists($path . $this->tplFile)) { throw new HtmlFormException("Template file '{$path}{$this->tplFile}' does not exist.", HtmlFormException::TEMPLATE_FILE_NOT_FOUND); } $this->template = @file_get_contents($path . $this->tplFile); return TRUE; }
/** * get list of users belonging to given admingroup * * @param string $admingroup_alias * @param callback $callBackSort * @throws UserException * * @return array [User] */ public static function getUsersBelongingToGroup($admingroup_alias, $callBackSort = NULL) { $users = array(); $rows = Application::getInstance()->getDb()->doPreparedQuery(' SELECT adminID FROM admin a INNER JOIN admingroups ag ON a.admingroupsID = ag.admingroupsID WHERE UPPER(ag.alias) = ? ', array(strtoupper($admingroup_alias))); foreach ($rows as $r) { $users[] = User::getInstance($r['adminID']); } if (is_null($callBackSort)) { return $users; } else { if (is_callable($callBackSort)) { usort($users, $callBackSort); return $users; } else { if (is_callable("UserAbstract::{$callBackSort}")) { usort($users, "UserAbstract::{$callBackSort}"); return $users; } else { throw new UserException("'{$callBackSort}' is not callable.", UserException::SORT_CALLBACK_NOT_CALLABLE); } } } }