<?php
namespace XF;
use XF\Http;
use XF\Mvc\Dispatcher;
use XF\Mvc\Renderer\AbstractRenderer;
use XF\Mvc\Reply\AbstractReply;
use XF\Mvc\Reply\Redirect;
use XF\Mvc\RouteMatch;
use XF\Template\Compiler;
use XF\Template\Templater;
use XF\Util\File;
use function func_get_args, get_class, intval, is_array, is_scalar, strlen, strval;
class App implements \ArrayAccess
{
protected $container;
protected $preLoadShared = [
'addOns',
'addOnsComposer',
'autoJobRun',
'bbCodeMedia',
'classExtensions',
'codeEventListeners',
'connectedAccountCount',
'contentTypes',
'displayStyles',
'helpPageCount',
'languages',
'masterStyleProperties',
'nodeTypes',
'options',
'reactions',
'reportCounts',
'simpleCache',
'smilies',
'unapprovedCounts',
'userBanners',
'userTitleLadder',
'userUpgradeCount',
'widgetCache',
'widgetDefinition',
'widgetPosition'
];
protected $preLoadLocal = [];
public function __construct(Container $container)
{
$this->container = $container;
$this->initialize();
}
protected function initialize()
{
$time = !empty($_SERVER["REQUEST_TIME_FLOAT"]) ? $_SERVER["REQUEST_TIME_FLOAT"] : microtime(true);
$container = $this->container;
$container['time'] = intval($time);
$container['time.granular'] = $time;
$container['app.classType'] = '';
$container['app.defaultType'] = '';
$container['config.default'] = [
'db' => [
'adapterClass' => 'XF\Db\Mysqli\Adapter'
],
'fullUnicode' => false,
'cache' => [
'enabled' => false,
'sessions' => false,
'namespace' => 'xf',
'provider' => 'Void',
'config' => [],
'context' => []
],
'pageCache' => [
'enabled' => false,
'lifetime' => 300,
'recordSessionActivity' => true,
'routeMatches' => [],
'onSetup' => null
],
'debug' => false,
'development' => [
'enabled' => false,
'defaultAddOn' => '',
'skipAddOns' => null, // this has to be something other than an array to allow people to change it
'unlistedAddOns' => [], // simply prevents the noted add-ons being listed as available
'throwJobErrors' => false,
'fullJs' => false,
'fullEditorJs' => false, // internal use only, the necessary files are not distributed
'closureCompilerPath' => null,
],
'designer' => [
'enabled' => false,
'basePath' => 'src' . \XF::$DS . 'styles'
],
'cookie' => [
'prefix' => 'xf_',
'path' => '/',
'domain' => ''
],
'http' => [
'sslVerify' => null,
'proxy' => null
],
'globalSalt' => '0dc6b46092d2e0461564217fad77cf31',
'superAdmins' => '', // keep this for upgrade purposes
'internalDataPath' => 'internal_data',
'codeCachePath' => '%s/code_cache',
'tempDataPath' => '%s/temp',
'fsAdapters' => [],
'externalDataPath' => 'data',
'externalDataUrl' => 'data',
'javaScriptUrl' => 'js',
'chmodWritableValue' => 0,
'forceCliUser' => '',
'jobMaxRunTime' => 8,
'enableMail' => true,
'enableMailQueue' => true,
'enableListeners' => true,
'enableTemplateModificationCallbacks' => true,
'enableClickjackingProtection' => true,
'enableReverseTabnabbingProtection' => true,
'enableLoginCsrf' => true,
'enableGzip' => true,
'enableContentLength' => true,
'enableTfa' => true,
'enableLivePayments' => true,
'enableApi' => true,
'enableAddOnArchiveInstaller' => false,
'enableOneClickUpgrade' => true,
'maxImageResizePixelCount' => 20000000,
'adminLogLength' => 60, // number of days to keep admin log entries
'adminColorHueShift' => 0,
'checkVersion' => true,
'passwordIterations' => 10,
'auth' => null,
'proxyUrlFormat' => 'proxy.php?{type}={url}&hash={hash}',
'sendmailPath' => null,
'serviceUnavailableCode' => 503,
'serviceWorkerPath' => null,
'sessionActivityExpiration' => 3600,
'sessionActivityOptimized' => true, // works around MySQL locking issues
'container' => [],
'exists' => false
];
$container['config.file'] = \XF::getSourceDirectory() . '/config.php';
$container['config.legacyFile'] = \XF::getRootDirectory() . '/library/config.php';
$container['config'] = function (Container $c)
{
$default = $c['config.default'];
$file = $c['config.file'];
$legacyFile = $c['config.legacyFile'];
if (file_exists($file))
{
$config = [];
require($file);
$config = array_replace_recursive($default, $config);
$config['exists'] = true;
$config['legacyExists'] = null;
}
else
{
if (file_exists($legacyFile))
{
$config = [];
require($legacyFile);
$config = array_replace_recursive($default, $config);
$config['legacyExists'] = true;
}
else
{
$config = $default;
$config['legacyExists'] = false;
}
}
// if there's a specific session cache specified, force this to be enabled
if (!empty($config['cache']['context']['sessions']))
{
$config['cache']['sessions'] = true;
}
foreach ($config['container'] AS $key => $value)
{
$c[$key] = $value;
}
return $config;
};
$container['jQueryVersion'] = '3.5.1';
$container['jsVersion'] = function (Container $c)
{
return substr(md5(\XF::$versionId . $c['options']->jsLastUpdate), 0, 8);
};
$container['avatarSizeMap'] = [
'o' => 384,
'h' => 384,
'l' => 192,
'm' => 96,
's' => 48
];
$container['editorToolbarSizes'] = [
'SM' => 420,
'MD' => 575,
'LG' => 900
];
$container['profileBannerSizeMap'] = [
'l' => 1280,
'm' => 640
];
$container['request'] = function (Container $c)
{
$request = new Http\Request($c['inputFilterer']);
$request->setCookiePrefix($c['config']['cookie']['prefix']);
return $request;
};
$container['request.paths'] = function (Container $c)
{
if (PHP_SAPI === 'cli')
{
$canonical = $c['options']->boardUrl;
$urlPath = parse_url($canonical, PHP_URL_PATH) ?? '';
$rootFullPath = $fullPath = $canonical;
$rootBasePath = $basePath = $canonical ? $urlPath : '';
}
else
{
/** @var Http\Request $request */
$request = $c['request'];
$fullPath = $request->getFullBasePath();
$basePath = $request->getBasePath();
// Protect against the request object being from an older release
// that didn't have these methods.
if (method_exists($request, 'getFullXfRootPath'))
{
$rootFullPath = $request->getFullXfRootPath();
$rootBasePath = $request->getXfRootPath();
}
else
{
$rootFullPath = $fullPath;
$rootBasePath = $basePath;
}
$canonical = $c['options']->boardUrl ?? $rootFullPath;
}
return [
'full' => rtrim($fullPath, '/') . '/',
'base' => rtrim($basePath, '/') . '/',
'root-full' => rtrim($rootFullPath, '/') . '/',
'root-base' => rtrim($rootBasePath, '/') . '/',
'canonical' => rtrim($canonical, '/') . '/',
'nopath' => '',
];
};
$container['request.pather'] = function (Container $c)
{
$paths = $c['request.paths'];
return function($url, $modifier = 'base') use ($paths)
{
if (preg_match('#^(/|[a-z]+:)#i', $url))
{
return $url;
}
if (isset($paths[$modifier]))
{
$url = $paths[$modifier] . $url;
}
else
{
$url = $paths['base'] . $url;
}
return $url;
};
};
$container['inlineImageTypes'] = function (Container $c)
{
return [
'gif' => 'image/gif',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'pjpeg' => 'image/pjpeg',
'png' => 'image/png'
];
};
$container['inlineVideoTypes'] = function (Container $c)
{
return [
'm4v' => 'video/mp4',
'mov' => 'video/quicktime',
'mp4' => 'video/mp4',
'mp4v' => 'video/mp4',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'ogv' => 'video/ogg',
'webm' => 'video/webm'
];
};
$container['inlineAudioTypes'] = function (Container $c)
{
return [
'mp3' => 'audio/mpeg',
'opus' => 'audio/ogg',
'ogg' => 'audio/ogg',
'wav' => 'audio/wav',
];
};
$container['response'] = function (Container $c)
{
/** @var \XF\Http\Request $request */
$request = $c['request'];
$cookie = $c['config']['cookie'];
$cookie['secure'] = $request->isSecure() ? true : false;
$response = new Http\Response();
$response->setCookieConfig($cookie);
$config = $c['config'];
if ($config['enableClickjackingProtection'])
{
$response->header('X-Frame-Options', 'SAMEORIGIN');
}
if (!$config['enableGzip'])
{
$response->compressIfAble(false);
}
if (!$config['enableContentLength'])
{
$response->includeContentLength(false);
}
return $response;
};
$container['inputFilterer'] = function(Container $c)
{
$class = $this->extendClass('XF\InputFilterer');
return new $class($c['config']['fullUnicode']);
};
$container['dispatcher'] = function ()
{
$class = $this->extendClass('XF\Mvc\Dispatcher');
return new $class($this);
};
$container['router'] = function (Container $c)
{
return $c['router.public'];
};
$container['router.public'] = function (Container $c)
{
$class = $this->extendClass('XF\Mvc\Router');
/** @var \XF\Mvc\Router $r */
$r = new $class($c['router.public.formatter'], $c['router.public.routes']);
$r->addRoutePreProcessor('filter', [$r, 'routePreProcessRouteFilter']);
$r->addRoutePreProcessor('extension', [$r, 'routePreProcessExtension']);
$r->addRoutePreProcessor('type', [$r, 'routePreProcessResponseType']);
$routeFilters = $c['routeFilters'];
$r->setRouteFilters($routeFilters['in'], $routeFilters['out']);
$r->setPather($c['request.pather']);
$r->setIndexRoute($c['options']->indexRoute ?: 'forums/');
$r->setIncludeTitleInUrls($c['options']->includeTitleInUrls);
$r->setRomanizeUrls($c['options']->romanizeUrls);
$this->fire('router_public_setup', [$c, &$r]);
return $r;
};
$container['router.public.formatter'] = function ($c)
{
if ($c['options']->useFriendlyUrls)
{
return function($route, $queryString)
{
return $route . (strlen($queryString) ? '?' . $queryString : '');
};
}
else
{
return function($route, $queryString)
{
$suffix = $route . (strlen($queryString) ? (strlen($route) ? '&' : '') . $queryString : '');
return strlen($suffix) ? 'index.php?' . $suffix : 'index.php';
};
}
};
$container['router.public.routes'] = $this->fromRegistry('routesPublic',
function(Container $c) { return $c['em']->getRepository('XF:Route')->rebuildRouteCache('public'); }
);
$container['router.install'] = function (Container $c)
{
$class = $this->extendClass('XF\Mvc\Router');
/** @var \XF\Mvc\Router $r */
$r = new $class($c['router.install.formatter'], $c['router.install.routes']);
$r->addRoutePreProcessor('extension', [$r, 'routePreProcessExtension']);
$r->addRoutePreProcessor('type', [$r, 'routePreProcessResponseType']);
$r->setPather($c['request.pather']);
return $r;
};
$container['router.install.formatter'] = $container->wrap(function($route, $queryString)
{
$suffix = $route . (strlen($queryString) ? '&' . $queryString : '');
return strlen($suffix) ? 'index.php?' . $suffix : 'index.php';
});
$container['router.install.routes'] = function (Container $c)
{
return [
'install' => [
'' => [
'controller' => 'XF:Install',
'format' => ''
]
],
'upgrade' => [
'' => [
'controller' => 'XF:Upgrade',
'format' => ''
]
],
'index' => [
'' => [
'controller' => 'XF:Index',
'format' => ''
]
]
];
};
$container['router.admin'] = function (Container $c)
{
$class = $this->extendClass('XF\Mvc\Router');
/** @var \XF\Mvc\Router $r */
$r = new $class($c['router.admin.formatter'], $c['router.admin.routes']);
$r->addRoutePreProcessor('extension', [$r, 'routePreProcessExtension']);
$r->addRoutePreProcessor('type', [$r, 'routePreProcessResponseType']);
$r->setPather($c['request.pather']);
$r->setRomanizeUrls($c['options']->romanizeUrls);
return $r;
};
$container['router.admin.formatter'] = $container->wrap(function($route, $queryString)
{
$suffix = $route . (strlen($queryString) ? '&' . $queryString : '');
return strlen($suffix) ? 'admin.php?' . $suffix : 'admin.php';
});
$container['router.admin.routes'] = $this->fromRegistry('routesAdmin',
function(Container $c) { return $c['em']->getRepository('XF:Route')->rebuildRouteCache('admin'); }
);
$container['router.api'] = function (Container $c)
{
$class = $this->extendClass('XF\Api\Mvc\Router');
/** @var \XF\Api\Mvc\Router $r */
$r = new $class($c['router.api.formatter'], $c['router.api.routes']);
$r->addRoutePreProcessor('apiPrefix', [$r, 'routePreProcessApiPrefix']);
$r->addRoutePreProcessor('apiVersion', [$r, 'routePreProcessApiVersion']);
$r->addRoutePreProcessor('apiType', [$r, 'routePreProcessApiResponseType']);
$r->setPather($c['request.pather']);
return $r;
};
$container['router.api.formatter'] = function ($c)
{
// Note: always enforcing friendly URLs for the API for consistency.
//if ($c['options']->useFriendlyUrls)
if (true)
{
return function($route, $queryString)
{
return 'api/' . $route . (strlen($queryString) ? '?' . $queryString : '');
};
}
else
{
return function($route, $queryString)
{
$suffix = $route . (strlen($queryString) ? (strlen($route) ? '&' : '') . $queryString : '');
return 'index.php?api/' . $suffix;
};
}
};
$container['router.api.routes'] = $this->fromRegistry('routesApi',
function(Container $c) { return $c['em']->getRepository('XF:Route')->rebuildRouteCache('api'); }
);
$container['logger'] = function($c)
{
$class = $this->extendClass('XF\Logger');
return new $class($this);
};
$container['debugger'] = function($c)
{
$class = $this->extendClass('XF\Debugger');
return new $class($this);
};
$container['contactUrl'] = function ($c)
{
$options = $c['options'];
$router = $c['router.public'];
if (!isset($options->contactUrl['type']))
{
return '';
}
switch ($options->contactUrl['type'])
{
case 'default': $url = $router->buildLink('misc/contact'); break;
case 'custom': $url = $options->contactUrl['custom']; break;
default: $url = '';
}
return $url;
};
$container['privacyPolicyUrl'] = function ($c)
{
$options = $c['options'];
$router = $c['router.public'];
if (!isset($options->privacyPolicyUrl['type']))
{
return '';
}
switch ($options->privacyPolicyUrl['type'])
{
case 'default':
return $router->buildLink('help/privacy-policy/'); break;
case 'custom':
return $options->privacyPolicyUrl['custom']; break;
default:
return '';
}
};
$container['tosUrl'] = function ($c)
{
$options = $c['options'];
$router = $c['router.public'];
if (!isset($options->tosUrl['type']))
{
return '';
}
switch ($options->tosUrl['type'])
{
case 'default': $url = $router->buildLink('help/terms/'); break;
case 'custom': $url = $options->tosUrl['custom']; break;
default: $url = '';
}
return $url;
};
$container['homePageUrl'] = function(Container $c)
{
$options = $c['options'];
$router = $c['router.public'];
$homePageUrl = $options->homePageUrl;
$this->extension()->fire('home_page_url', [&$homePageUrl, $router]);
if ($homePageUrl)
{
/** @var \Closure $pather */
$pather = $c['request.pather'];
$homePageUrl = $pather($homePageUrl, 'full');
}
return $homePageUrl;
};
$container['navigation.compiler'] = function(Container $c)
{
return new \XF\Navigation\Compiler($c['templateCompiler']);
};
$container['navigation.file'] = 'navigation_cache.php'; // will be written to code-cache/codeCachePath
$container['navigation.admin'] = function($c)
{
$class = \XF::extendClass('XF\AdminNavigation');
return new $class($c['navigation.adminEntries']);
};
$container['navigation.adminEntries'] = $this->fromRegistry('adminNavigation',
function(Container $c) { return $c['em']->getRepository('XF:AdminNavigation')->rebuildNavigationCache(); }
);
$container['db'] = function ($c)
{
$config = $c['config'];
$dbConfig = $config['db'];
$adapterClass = $dbConfig['adapterClass'];
unset($dbConfig['adapterClass']);
/** @var \XF\Db\AbstractAdapter $db */
$db = new $adapterClass($dbConfig, $config['fullUnicode']);
if ($db instanceof \XF\Db\ForeignAdapter)
{
throw new \LogicException('This database adapter cannot be used natively by XenForo.');
}
if (\XF::$debugMode)
{
$db->logQueries(true);
}
return $db;
};
$container->factory('cache', function($context, array $params, Container $c)
{
$cacheConfig = $c['config']['cache'];
if (!$cacheConfig['enabled'])
{
return null;
}
$namespace = $cacheConfig['namespace'];
if ($context)
{
if (empty($cacheConfig['context'][$context]))
{
return null;
}
$cacheConfig = $cacheConfig['context'][$context];
if (!is_array($cacheConfig) || empty($cacheConfig['provider']))
{
return null;
}
if (!isset($cacheConfig['config']))
{
$cacheConfig['config'] = [];
}
if (isset($cacheConfig['namespace']))
{
$namespace = $cacheConfig['namespace'];
}
}
/** @var CacheFactory $factory */
$factory = $c['cache.factory'];
$factory->setNamespace($namespace);
return $factory->create($cacheConfig['provider'], $cacheConfig['config']);
});
$container['cache'] = function(Container $c)
{
return $c->create('cache', '');
};
$container['cache.factory'] = function($c)
{
// this cannot be dynamically extended because it's used by the registry
// different types of cache providers can be configured via code in config.php
return new CacheFactory();
};
$container['permission.cache'] = function ($c)
{
return new PermissionCache($c['db']);
};
$container['permission.builder'] = function ($c)
{
return new \XF\Permission\Builder(
$c['db'], $c['em'], $this->getContentTypeField('permission_handler_class')
);
};
$container['registry'] = function ($c)
{
return new DataRegistry($c['db'], $this->cache('registry'));
};
$container['simpleCache'] = function ($c)
{
$class = $this->extendClass('XF\SimpleCache');
return new $class($c['simpleCache.data']);
};
$container['simpleCache.data'] = $this->fromRegistry('simpleCache', function() {
$this->registry()->set('simpleCache', []);
return [];
});
$container['options'] = $this->fromRegistry('options',
function(Container $c) { return $c['em']->getRepository('XF:Option')->rebuildOptionCache(); },
function(array $options)
{
return new \ArrayObject($options, \ArrayObject::ARRAY_AS_PROPS);
}
);
$container['codeEventListeners'] = $this->fromRegistry('codeEventListeners',
function(Container $c) { return $c['em']->getRepository('XF:CodeEventListener')->rebuildListenerCache(); }
);
$container['contentTypes'] = $this->fromRegistry('contentTypes',
function(Container $c) { return $c['em']->getRepository('XF:ContentTypeField')->rebuildContentTypeCache(); }
);
$container['customFields.threads'] = $this->fromRegistry('threadFieldsInfo',
function(Container $c) { return $c['em']->getRepository('XF:ThreadField')->rebuildFieldCache(); },
function(array $threadFieldsInfo)
{
$class = 'XF\CustomField\DefinitionSet';
$class = $this->extendClass($class);
return new $class($threadFieldsInfo);
}
);
$container['customFields.users'] = $this->fromRegistry('userFieldsInfo',
function(Container $c) { return $c['em']->getRepository('XF:UserField')->rebuildFieldCache(); },
function(array $userFieldsInfo)
{
$class = 'XF\CustomField\DefinitionSet';
$class = $this->extendClass($class);
$definitionSet = new $class($userFieldsInfo);
$definitionSet->addFilter('registration', function(array $field)
{
return (!empty($field['show_registration']) || !empty($field['required']));
});
$definitionSet->addFilter('profile', function(array $field)
{
return !empty($field['viewable_profile']);
});
$definitionSet->addFilter('message', function(array $field)
{
return !empty($field['viewable_message']);
});
return $definitionSet;
}
);
$container['displayStyles'] = $this->fromRegistry('displayStyles',
function(Container $c) { return $c['em']->getRepository('XF:UserGroup')->rebuildDisplayStyleCache(); }
);
$container['reportCounts'] = $this->fromRegistry('reportCounts',
function(Container $c) { return $c['em']->getRepository('XF:Report')->rebuildReportCounts(); }
);
$container['unapprovedCounts'] = $this->fromRegistry('unapprovedCounts',
function(Container $c) { return $c['em']->getRepository('XF:ApprovalQueue')->rebuildUnapprovedCounts(); }
);
$container['nodeTypes'] = $this->fromRegistry('nodeTypes',
function(Container $c) { return $c['em']->getRepository('XF:NodeType')->rebuildNodeTypeCache(); }
);
$container['notices'] = $this->fromRegistry('notices',
function(Container $c) { return $c['em']->getRepository('XF:Notice')->rebuildNoticeCache(); }
);
$container['notices.lastReset'] = $this->fromRegistry('noticesLastReset',
function(Container $c) { return $c['em']->getRepository('XF:Notice')->rebuildNoticeLastResetCache(); }
);
$container->factory('criteria', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Criteria\%s');
$class = $this->extendClass($class);
array_unshift($params, $this);
return $c->createObject($class, $params);
}, false);
$container['routeFilters'] = $this->fromRegistry('routeFilters',
function(Container $c) { return $c['em']->getRepository('XF:RouteFilter')->rebuildRouteFilterCache(); }
);
$container['reactionDefault'] = function(Container $c)
{
return $c['reactions'][1];
};
$container['reactionColors'] = function(Container $c)
{
$output = [];
foreach ($c['reactions'] AS $reactionId => $reaction)
{
$output[$reactionId] = $reaction['text_color'];
}
return $output;
};
$container['reactionSprites'] = $this->fromRegistry('reactionSprites',
function(Container $c) { return $c['em']->getRepository('XF:Reaction')->rebuildReactionSpriteCache(); }
);
$container['reactions'] = $this->fromRegistry('reactions',
function(Container $c) { return $c['em']->getRepository('XF:Reaction')->rebuildReactionCache(); }
);
$container['smilieSprites'] = $this->fromRegistry('smilieSprites',
function(Container $c) { return $c['em']->getRepository('XF:Smilie')->rebuildSmilieSpriteCache(); }
);
$container['smilies'] = $this->fromRegistry('smilies',
function(Container $c) { return $c['em']->getRepository('XF:Smilie')->rebuildSmilieCache(); }
);
$container['prefixes.thread'] = $this->fromRegistry('threadPrefixes',
function(Container $c) { return $c['em']->getRepository('XF:ThreadPrefix')->rebuildPrefixCache(); }
);
$container['userBanners'] = $this->fromRegistry('userBanners',
function(Container $c) { return $c['em']->getRepository('XF:UserGroup')->rebuildUserBannerCache(); }
);
$container['userTitleLadder'] = $this->fromRegistry('userTitleLadder',
function(Container $c) { return $c['em']->getRepository('XF:UserTitleLadder')->rebuildLadderCache(); }
);
$container['forumStatistics'] = $this->fromRegistry('forumStatistics',
function(Container $c) { return $c['em']->getRepository('XF:Counters')->rebuildForumStatisticsCache(); }
);
$container['connectedAccountCount'] = $this->fromRegistry('connectedAccountCount',
function(Container $c) { return $c['em']->getRepository('XF:ConnectedAccount')->rebuildProviderCount(); }
);
$container['helpPageCount'] = $this->fromRegistry('helpPageCount',
function(Container $c) { return $c['em']->getRepository('XF:HelpPage')->rebuildHelpPageCount(); }
);
$container['userUpgradeCount'] = $this->fromRegistry('userUpgradeCount',
function(Container $c) { return $c['em']->getRepository('XF:UserUpgrade')->rebuildUpgradeCount(); }
);
$container['session'] = function ()
{
throw new \LogicException('The session key must be overridden.');
};
$container['session.public'] = function (Container $c)
{
$class = $this->extendClass('XF\Session\Session');
/** @var \XF\Session\Session $session */
$session = new $class($c['session.public.storage'], [
'cookie' => 'session'
]);
return $session->start($c['request']);
};
$container['session.public.storage'] = function (Container $c)
{
$storage = null;
$cache = $c['cache'];
$this->fire('session_public_storage_setup', [$c, $cache, &$storage]);
if ($storage)
{
if (!($storage instanceof \XF\Session\StorageInterface))
{
throw new \LogicException('Storage must be instance of XF\Session\StorageInterface. Received ' . get_class($storage));
}
return $storage;
}
if ($c['config']['cache']['sessions'] && $cache = $this->cache('sessions'))
{
return new \XF\Session\CacheStorage($cache, 'session_');
}
else
{
return new \XF\Session\DbStorage($c['db'], 'xf_session');
}
};
$container['session.admin'] = function (Container $c)
{
$session = new \XF\Session\Session($c['session.admin.storage'], [
'cookie' => 'session_admin'
]);
return $session->start($c['request']);
};
$container['session.admin.storage'] = function (Container $c)
{
return new \XF\Session\DbStorage($c['db'], 'xf_session_admin');
};
$container['session.install'] = function (Container $c)
{
$session = new \XF\Session\Session($c['session.install.storage'], [
'cookie' => 'session_install'
]);
return $session->start($c['request']);
};
$container['session.install.storage'] = function (Container $c)
{
/** @var \XF\Db\SchemaManager $sm */
$sm = $c['db']->getSchemaManager();
try
{
if (!(bool)$sm->getTableStatus('xf_session_install'))
{
$mySql = new \XF\Install\Data\MySql();
$tables = $mySql->getTables();
$sm->createTable('xf_session_install', $tables['xf_session_install']);
}
}
catch (\Exception $e) {}
return new \XF\Session\DbStorage($c['db'], 'xf_session_install');
};
$container['session.api'] = function (Container $c)
{
$session = new \XF\Session\Session(new \XF\Session\NullStorage());
return $session->start($c['request']);
};
$container['csrf.token'] = function(Container $c)
{
/** @var Http\Request $request */
$request = $c['request'];
$token = $request->getCookie('csrf');
if (!$token)
{
$token = \XF::generateRandomString(16);
$this->updateCsrfCookie($token);
}
/** @var \Closure $validator */
$validator = $c['csrf.validator'];
return \XF::$time . ',' . $validator($token, \XF::$time);
};
$container['csrf.validator'] = $container->wrap(function($value, $time)
{
return hash_hmac('md5', $value . $time, $this->config('globalSalt'));
});
$container['error'] = function ()
{
return new Error($this);
};
$container['em'] = function (Container $c)
{
return new Mvc\Entity\Manager($c['db'], $c['em.valueFormatter'], $c['extension']);
};
$container['em.valueFormatter'] = function (Container $c)
{
return new Mvc\Entity\ValueFormatter();
};
$container['mailer'] = function (Container $c)
{
/** @var \ArrayObject $options */
$options = $c['options'];
$mailerClass = $this->extendClass('XF\Mail\Mailer');
/** @var \XF\Mail\Mailer $mailer */
$mailer = new $mailerClass($c['mailer.templater'], $c['mailer.transport'], $c['mailer.styler'], $c['mailer.queue']);
$mailer->setDefaultFrom(
$options->defaultEmailAddress,
$options->emailSenderName ?: $options->boardTitle
);
$mailer->setDefaultReturnPath(
$options->bounceEmailAddress ?: $options->defaultEmailAddress,
$options->enableVerp
);
$mailClass = $this->extendClass($mailer->getMailClass());
$mailer->setMailClass($mailClass);
$this->fire('mailer_setup', [$c, &$mailer]);
return $mailer;
};
$container['mailer.transport'] = function(Container $c)
{
$config = $c['config'];
if (!$config['enableMail'])
{
return new \Swift_NullTransport();
}
$transport = null;
$this->fire('mailer_transport_setup', [$c, &$transport]);
if ($transport)
{
return $transport;
}
/** @var \ArrayObject $options */
$options = $c['options'];
$mailerClass = $this->extendClass('XF\Mail\Mailer');
if (is_array($options->emailTransport))
{
$method = $options->emailTransport['emailTransport'];
$config = $options->emailTransport;
if (!empty($config['oauth']))
{
/** @var \XF\Repository\Option $optionRepo */
$optionRepo = $this->repository('XF:Option');
$config = $optionRepo->refreshEmailAccessTokenIfNeeded('emailTransport');
}
}
else
{
$method = 'sendmail';
$config = [];
}
return $mailerClass::getTransportFromOption($method, $config);
};
$container['mailer.styler'] = function($c)
{
$rendererClass = $this->extendClass('XF\CssRenderer');
$stylerClass = $this->extendClass('XF\Mail\Styler');
return new $stylerClass(
new $rendererClass($this, $c['mailer.templater'], $this->cache('css')),
new \Pelago\Emogrifier()
);
};
$container['mailer.queue'] = function(Container $c)
{
$config = $c['config'];
if (!$config['enableMailQueue'])
{
return null;
}
$queueClass = $this->extendClass('XF\Mail\Queue');
return new $queueClass($c['db']);
};
$container['mailer.templater'] = function (Container $c)
{
if ($c['app.classType'] != 'Pub')
{
// preload this in bulk as we will load them individually below
$this->registry()->get(['routesPublic', 'routeFilters', 'styles']);
}
$templater = $this->setupTemplaterObject($c, '\XF\Mail\Templater');
$templater->setStyle($c->create('style', $c['options']->defaultEmailStyleId));
return $templater;
};
$container['fs'] = function(Container $c)
{
$mountsClass = $this->extendClass('XF\FsMounts');
return $mountsClass::loadDefaultMounts($c['config']);
};
$container['spam'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Spam');
return new $class($c, $this);
};
$container['http'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Http');
return new $class($c, $this);
};
$container['oAuth'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\OAuth');
return new $class($c, $this);
};
$container['proxy'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Proxy');
return new $class($c, $this);
};
$container['oembed'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Oembed');
return new $class($c, $this);
};
$container['bounce'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Bounce');
return new $class($c, $this);
};
$container['unsubscribe'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Unsubscribe');
return new $class($c, $this);
};
$container['widget'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Widget');
return new $class($c, $this);
};
$container['import'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\Import');
return new $class($c, $this);
};
$container['webManifestRenderer'] = function($c)
{
$class = $this->extendClass('XF\WebManifestRenderer');
return new $class($this);
};
$container->set('sitemap.builder', function(Container $c)
{
$class = 'XF\Sitemap\Builder';
$class = $this->extendClass($class);
$user = $c['em']->getRepository('XF:User')->getGuestUser();
$types = $c['em']->getRepository('XF:SitemapLog')->getSitemapContentTypes();
return new $class($this, $user, $types);
}, false);
$container->set('sitemap.renderer', function(Container $c)
{
$sitemapRepo = $this->repository('XF:SitemapLog');
$class = $this->extendClass('XF\Sitemap\Renderer');
return new $class($this, $sitemapRepo->getActiveSitemap());
}, false);
$container['imageManager'] = function($c)
{
$manager = new \XF\Image\Manager($c['imageManager.defaultDriver'], $c['imageManager.extraDrivers']);
$manager->setMaxResizePixels($c['config']['maxImageResizePixelCount']);
return $manager;
};
$container['imageManager.defaultDriver'] = function($c)
{
return $c['options']->imageLibrary;
};
$container['imageManager.extraDrivers'] = [];
$container['adminSearcher'] = function($c)
{
$class = $this->extendClass('XF\AdminSearch\Searcher');
return new $class(
$this,
$this->getContentTypeField('admin_search_class')
);
};
$container['search'] = function($c)
{
$class = $this->extendClass('XF\Search\Search');
return new $class($c['search.source'], $this->getContentTypeField('search_handler_class'));
};
$container['search.source'] = function($c)
{
$source = null;
$this->fire('search_source_setup_22', [$c, &$source]);
if ($source)
{
if (!($source instanceof \XF\Search\Source\AbstractSource))
{
throw new \LogicException('Search source must be instance of XF\Search\Source\AbstractSource. Received ' . get_class($source));
}
return $source;
}
$mySqlFtClass = $this->extendClass('XF\Search\Source\MySqlFt');
return new $mySqlFtClass($c['db'], $c['options']->searchMinWordLength);
};
$container->factory('stats.grouper', function($grouping, array $params, Container $c)
{
$groupings = $c['stats.groupings'];
if (!isset($groupings[$grouping]))
{
throw new \InvalidArgumentException("Unknown grouping '$grouping'");
}
$grouperClass = \XF::stringToClass($groupings[$grouping], '%s\Stats\Grouper\%s');
$grouperClass = \XF::extendClass($grouperClass);
$language = $params[0] ?? \XF::language();
return new $grouperClass($language);
});
$container['stats.groupings'] = [
'daily' => 'XF:Daily',
'weekly' => 'XF:Weekly',
'monthly' => 'XF:Monthly'
];
$container->factory('language', function($id, array $params, Container $c)
{
$id = intval($id);
$cache = $c['language.cache'];
if (!$id || !isset($cache[$id]))
{
$id = $c['options']->defaultLanguageId ?? null;
}
if (isset($cache[$id]))
{
$groupPath = File::getCodeCachePath() . '/phrase_groups';
$class = $this->extendClass('XF\Language');
return new $class($id, $cache[$id], $c['db'], $groupPath);
}
else
{
return $c['language.fallback'];
}
});
$container['language.fallback'] = function(Container $c)
{
$groupPath = File::getCodeCachePath() . '/phrase_groups';
$class = $this->extendClass('XF\Language');
return new $class(0, [], $c['db'], $groupPath);
};
$container['language.cache'] = $this->fromRegistry('languages',
function(Container $c) { return $c['em']->getRepository('XF:Language')->rebuildLanguageCache(); }
);
$container['language.all'] = function(Container $c)
{
$output = [];
foreach (array_keys($c['language.cache']) AS $languageId)
{
$output[$languageId] = $c->create('language', $languageId);
}
return $output;
};
$container->factory('style', function($id, array $params, Container $c)
{
$id = intval($id);
$cache = $c['style.cache'];
if (!$id || !isset($cache[$id]))
{
$id = $c['options']->defaultStyleId;
}
if (isset($cache[$id]))
{
$style = $cache[$id];
$masterStyleProperties = $c['style.masterStyleProperties'];
if (is_array($style['properties']))
{
$style['properties'] += $masterStyleProperties;
}
else
{
$style['properties'] = $masterStyleProperties;
}
$class = $this->extendClass('XF\Style');
return new $class($id, $style);
}
else
{
return $c['style.fallback'];
}
});
$container['style.fallback'] = function(Container $c)
{
$lastModified = $c['style.masterModifiedDate'];
$masterStyleProperties = $c['style.masterStyleProperties'];
$class = $this->extendClass('XF\Style');
return new $class(0, $masterStyleProperties, $lastModified);
};
$container['style.masterModifiedDate'] = $this->fromRegistry('masterStyleModifiedDate',
function(Container $c) { return \XF::$time; }
);
$container['style.masterStyleProperties'] = $this->fromRegistry('masterStyleProperties',
function(Container $c) { return []; }
);
$container['style.cache'] = $this->fromRegistry('styles',
function(Container $c) { return $c['em']->getRepository('XF:Style')->rebuildStyleCache(); }
);
$container['style.all'] = function(Container $c)
{
$output = [];
foreach (array_keys($c['style.cache']) AS $styleId)
{
$output[$styleId] = $c->create('style', $styleId);
}
return $output;
};
$container['defaultNavigationId'] = function(Container $c)
{
return '_default';
};
$container['uploadMaxFilesize'] = function(Container $c)
{
return \XF\Util\Php::getUploadMaxFilesize();
};
// We do not recommend trying to extend this class or the tags/functions it defines. Doing so may interfere
// with the upgrade process. If you must do so, it should not be part of an add-on. It should be done
// unconditionally via config.php. However, do so at your own peril as the worst case would potentially be
// an un-upgradeable installation.
$container['templateCompiler'] = function (Container $c)
{
return new Template\Compiler();
};
$container['templater'] = function (Container $c)
{
return $this->setupTemplaterObject($c, '\XF\Template\Templater');
};
// the default config is in the templater
$container['templater.config.filters'] = [];
$container['templater.config.functions'] = [];
$container['templater.config.tests'] = [];
$container['cssWriter'] = function($c)
{
$rendererClass = $this->extendClass('XF\CssRenderer');
$renderer = new $rendererClass($this, $c['templater'], $this->cache('css'));
$class = $this->extendClass('XF\CssWriter');
/** @var \XF\CssWriter $writer */
$writer = new $class($this, $renderer);
$writer->setValidator($c['css.validator']);
return $writer;
};
$container['css.validator'] = $container->wrap(function(array $templates)
{
return hash_hmac('sha1', implode(',', $templates), $this->config('globalSalt'));
});
$container['addon.manager'] = function($c)
{
$class = $this->extendClass('XF\AddOn\Manager');
return new $class(\XF::getAddOnDirectory());
};
$container['addon.dataManager'] = function($c)
{
$class = $this->extendClass('XF\AddOn\DataManager');
return new $class($c['em']);
};
$container['addon.cache'] = $this->fromRegistry('addOns',
function(Container $c) { return $c['addon.dataManager']->rebuildActiveAddOnCache(); }
);
$container['addon.composer'] = $this->fromRegistry('addOnsComposer',
function(Container $c)
{
$c['addon.dataManager']->rebuildActiveAddOnCache();
return $this->container['registry']['addOnsComposer'];
}
);
$container['giphy.api'] = function ($c)
{
$giphyOption = $c['options']['giphy'];
if (!$giphyOption['enabled'] || !$giphyOption['api_key'])
{
return null;
}
$class = $this->extendClass('XF\Giphy\Api');
return new $class($giphyOption['api_key'], [
'rating' => $giphyOption['rating'],
'lang' => substr(\XF::language()->getLanguageCode(), 0, 2), // note: invalid values ignored
'random_id' => null, // TODO: system to fetch and maintain per-user random ID (xf_user_connected_account?) optional though
]);
};
$container['bannedEmails'] = $this->fromRegistry('bannedEmails',
function(Container $c) { return $c['em']->getRepository('XF:Banning')->rebuildBannedEmailCache(); }
);
$container['bannedIps'] = $this->fromRegistry('bannedIps',
function(Container $c) { return $c['em']->getRepository('XF:Banning')->rebuildBannedIpCache(); }
);
$container['discouragedIps'] = $this->fromRegistry('discouragedIps',
function(Container $c) { return $c['em']->getRepository('XF:Banning')->rebuildDiscouragedIpCache(); }
);
$container['forumTypes'] = $this->fromRegistry('forumTypes',
function(Container $c) { return $c['em']->getRepository('XF:ForumType')->rebuildForumTypeCache(); }
);
$container->factory('forumType', function($type, array $params, Container $c)
{
$forumTypes = $c['forumTypes'];
if (!isset($forumTypes[$type]))
{
return null;
}
$typeClass = \XF::stringToClass($forumTypes[$type], '%s\ForumType\%s');
$typeClass = \XF::extendClass($typeClass);
return new $typeClass($type);
});
$container['threadTypes'] = $this->fromRegistry('threadTypes',
function(Container $c) { return $c['em']->getRepository('XF:ThreadType')->rebuildThreadTypeCache(); }
);
$container->factory('threadType', function($type, array $params, Container $c)
{
$threadTypes = $c['threadTypes'];
if (!isset($threadTypes[$type]))
{
return null;
}
$typeClass = \XF::stringToClass($threadTypes[$type], '%s\ThreadType\%s');
$typeClass = \XF::extendClass($typeClass);
return new $typeClass($type);
});
$container['string.formatter'] = function(Container $c)
{
$class = $this->extendClass('XF\Str\Formatter');
/** @var \XF\Str\Formatter $formatter */
$formatter = new $class();
$options = $c['options'];
$formatter->setCensorRules($options->censorWords ?: [], $options->censorCharacter);
$formatter->addSmilies($c['smilies']);
$formatter->setSmilieHtmlPather($c['request.pather']);
$formatter->setProxyHandler(function($type, $url, array $options = [])
{
return $this->proxy()->generateExtended($type, $url, $options);
});
$this->fire('string_formatter_setup', [$c, &$formatter]);
return $formatter;
};
$container['bbCode'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\BbCode');
return new $class($c, $this);
};
$container['apiDocs'] = function($c)
{
$class = $this->extendClass('XF\SubContainer\ApiDocs');
return new $class($c, $this);
};
$container['job.manager'] = function($c)
{
$class = $this->extendClass('XF\Job\Manager');
return new $class($this, $c['job.manual.allow'], $c['job.manual.force']);
};
$container['job.runTime'] = $this->fromRegistry('autoJobRun',
function(Container $c) { return $c['job.manager']->updateNextRunTime(); }
);
$container['job.manual.allow'] = false;
$container['job.manual.force'] = false;
$container['development.output'] = function(Container $c)
{
$config = $c['config'];
$skip = $config['development']['skipAddOns'];
if (!is_array($skip))
{
$skip = ['XF', 'XF*'];
}
$class = $this->extendClass('XF\DevelopmentOutput');
return new $class(
$config['development']['enabled'],
\XF::getAddOnDirectory(),
$skip
);
};
$container['development.jsResponse'] = function(Container $c)
{
$class = $this->extendClass('XF\DevJsResponse');
return new $class($this);
};
$container['designer.output'] = function(Container $c)
{
$config = $c['config'];
$class = $this->extendClass('XF\DesignerOutput');
return new $class(
$config['designer']['enabled'],
$config['designer']['basePath']
);
};
$container['extension'] = function(Container $c)
{
$config = $c['config'];
if (!$config['enableListeners'])
{
// disable
return new \XF\Extension();
}
try
{
$listeners = $c['extension.listeners'];
$classExtensions = $c['extension.classExtensions'];
}
catch (\XF\Db\Exception $e)
{
$listeners = [];
$classExtensions = [];
}
return new \XF\Extension($listeners, $classExtensions);
};
// note: these don't trigger normal rebuilds to prevent the possibility of an infinite loop
$container['extension.listeners'] = $this->fromRegistry('codeEventListeners',
function(Container $c) { $c['registry']->set('codeEventListeners', []); return []; }
);
$container['extension.classExtensions'] = $this->fromRegistry('classExtensions',
function(Container $c) { $c['registry']->set('classExtensions', []); return []; }
);
$container->factory('controller', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '%s\%s\Controller\%s', $c['app.classType']);
$class = $this->extendClass($class);
$passParams = [
$this,
$params['request'] ?? $c['request']
];
return $c->createObject($class, $passParams, true);
}, false);
$container->factory('renderer', function($type, array $params, Container $c)
{
$type = strtolower($type);
switch ($type)
{
case 'html': $class = 'Html'; break;
case 'json': $class = 'Json'; break;
case 'xml': $class = 'Xml'; break;
case 'raw': $class = 'Raw'; break;
case 'rss': $class = 'Rss'; break;
default:
$unknownCallback = $c['renderer.unknown'];
$class = $unknownCallback($type);
}
$params = [
$c->getInvokableFactory('view'),
$c['response'],
$c['templater']
];
if (strpos($class, '\\') === false)
{
$class = 'XF\Mvc\Renderer\\' . $class;
}
$class = $this->extendClass($class);
return $c->createObject($class, $params);
}, false);
$container['renderer.unknown'] = function()
{
return function($rendererType)
{
return 'Html';
};
};
$container['view.defaultClass'] = 'XF\Mvc\View';
$container->factory('view', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '%s\%s\View\%s', $c['app.classType']);
$class = $this->extendClass($class, $c['view.defaultClass']);
if (!$class || !class_exists($class))
{
$class = $c['view.defaultClass'];
}
return $c->createObject($class, $params);
}, false);
$container->factory('job', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Job\%s');
$class = $this->extendClass($class);
array_unshift($params, $this);
return $c->createObject($class, $params, true);
}, false);
$container->factory('searcher', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Searcher\%s');
$class = $this->extendClass($class);
array_unshift($params, $c['em']);
return $c->createObject($class, $params);
}, false);
$container->factory('filterer', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Filterer\%s');
$class = $this->extendClass($class);
return $c->createObject($class, $params);
}, false);
$container->factory('auth', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Authentication\%s');
$class = $this->extendClass($class);
return $c->createObject($class, $params);
}, false);
$container['auth.default'] = 'XF:Core12';
$container->factory('service', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Service\%s');
$class = $this->extendClass($class);
array_unshift($params, $this);
return $c->createObject($class, $params);
}, false);
$container->factory('helper', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Helper\%s');
$class = $this->extendClass($class);
return $c->createObject($class, $params);
}, false);
$container->factory('validator', function($class, array $params, Container $c)
{
if (strpos($class, ':') === false && strpos($class, '\\') === false)
{
$class = "XF:$class";
}
$class = \XF::stringToClass($class, '\%s\Validator\%s');
$class = $this->extendClass($class);
array_unshift($params, $this);
return $c->createObject($class, $params);
}, false);
$container->factory('data', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Data\%s');
$class = $this->extendClass($class);
array_unshift($params, $this);
return $c->createObject($class, $params);
}, true);
$container->factory('captcha', function($class, array $params, Container $c)
{
if (strpos($class, ':') === false && strpos($class, '\\') === false)
{
$class = "XF:$class";
}
$class = \XF::stringToClass($class, '\%s\Captcha\%s');
if (!class_exists($class))
{
$this->error()->logError('CAPTCHA class ' . htmlspecialchars($class) . ' does not exist. Falling back to default provider \\' . htmlspecialchars($c['captcha.default']) . '.');
$class = $c['captcha.default'];
}
$class = $this->extendClass($class);
array_unshift($params, $this);
return $c->createObject($class, $params);
}, true);
$container['captcha.default'] = 'XF\Captcha\HCaptcha';
$container->factory('notifier', function($class, array $params, Container $c)
{
$class = \XF::stringToClass($class, '\%s\Notifier\%s');
$class = $this->extendClass($class);
array_unshift($params, $this);
return $c->createObject($class, $params);
}, false);
if (function_exists('xdebug_disable'))
{
xdebug_disable(); // use PHP's own stack trace in case of errors
}
$this->initializeExtra();
}
/**
* @param $key
* @param \Closure $rebuildFunction
* @param \Closure|null $decoratorFunction
*
* @return \Closure
* @throws \XF\Db\Exception
*/
public function fromRegistry($key, \Closure $rebuildFunction, \Closure $decoratorFunction = null)
{
return function(Container $c) use ($key, $rebuildFunction, $decoratorFunction)
{
$data = $this->container['registry'][$key];
if ($data === null)
{
$data = $rebuildFunction($c, $key);
}
return $decoratorFunction ? $decoratorFunction($data, $c, $key) : $data;
};
}
/**
* @param Container $c
* @param string $class
*
* @return Templater
* @throws \Exception
*/
public function setupTemplaterObject(Container $c, $class)
{
$config = $c['config'];
$class = $this->extendClass($class);
/** @var \XF\Template\Templater $templater */
$templater = new $class(
$this,
\XF::language(),
File::getCodeCachePath() . '/templates'
);
$templater->addDefaultHandlers();
$templater->addFilters($c['templater.config.filters']);
$templater->addFunctions($c['templater.config.functions']);
$templater->addTests($c['templater.config.tests']);
if ($config['development']['enabled'])
{
$templater->addTemplateWatcher($c['development.output']->getHandler('XF:Template'));
}
if ($config['designer']['enabled'])
{
$templater->addTemplateWatcher($c['designer.output']->getHandler('XF:Template'));
}
$templater->setCssValidator($c['css.validator']);
$options = $c['options'];
$templater->setJquerySource($c['jQueryVersion'], $options->jQuerySource);
$templater->setJsVersion($c['jsVersion']);
$templater->setJsBaseUrl($config['javaScriptUrl']);
$templater->setDynamicDefaultAvatars($options->dynamicAvatarEnable);
$templater->setMediaSites($c['bbCode.media']);
$templater->setUserTitleLadder($c['userTitleLadder'], $options->userTitleLadderField);
$templater->setUserBanners($c['userBanners'], $options->userBanners ?: []);
$templater->setGroupStyles($c['displayStyles']);
$templater->setWidgetPositions($c['widget.widgetPosition'] ?: []);
$this->fire('templater_setup', [$c, &$templater]);
return $templater;
}
public function initializeExtra()
{
}
public function setup(array $options = [])
{
$config = $this->container('config');
if (!$config['exists'])
{
if ($config['legacyExists'])
{
echo 'The site is currently being upgraded. Please check back later.';
exit;
}
else if (\XF\Util\File::installLockExists())
{
echo "Couldn't load src/config.php file.";
exit;
}
else
{
header('Location: install/index.php');
exit;
}
}
$this->checkDebugMode();
$this->checkDbWriteForced();
$preLoadExtra = $options['preLoad'] ?? [];
$this->preLoadData($preLoadExtra);
$this->setupAddOnComposerAutoload();
$this->fire('app_setup', [$this]);
}
protected function preLoadData(array $typeSpecific = [])
{
try
{
$keys = array_merge($this->preLoadShared, $this->preLoadLocal, $typeSpecific);
$this->registry()->get($keys);
}
catch (\Exception $e) {}
}
public function start($allowShortCircuit = false)
{
return null;
}
protected function getVisitorFromSession(\XF\Session\Session $session, array $extraWith = [])
{
$userRepo = $this->repository('XF:User');
$sessionUserId = $session->userId;
$user = $userRepo->getVisitor($sessionUserId, $extraWith);
if ($user->user_id && $user->user_id == $sessionUserId)
{
$userPasswordDate = $user->Profile ? $user->Profile->password_date : 0;
if ($session->passwordDate != $userPasswordDate)
{
$session->logoutUser();
$user = $userRepo->getVisitor(0);
}
}
return $user;
}
public function preDispatch(RouteMatch $match)
{
}
public function postDispatch(AbstractReply $reply, RouteMatch $finalMatch, RouteMatch $originalMatch)
{
}
public function preRender(AbstractReply $reply, $responseType)
{
$this->templater()->addDefaultParam('xf', $this->getGlobalTemplateData($reply));
}
public function getCustomFields($type, $group = null, array $onlyInclude = null, array $additionalFilters = [])
{
/** @var \XF\CustomField\DefinitionSet $definitionSet */
$definitionSet = $this->container["customFields.$type"];
if (!$definitionSet)
{
return null;
}
if ($group !== null)
{
$definitionSet = $definitionSet->filterGroup($group);
}
if (is_array($onlyInclude))
{
$definitionSet = $definitionSet->filterOnly($onlyInclude);
}
if ($additionalFilters)
{
$definitionSet = $definitionSet->filter($additionalFilters);
}
return $definitionSet;
}
public function getCustomFieldsForEdit(
$type, \XF\CustomField\Set $set, $editMode = 'user',
$group = null, array $onlyInclude = null, array $additionalFilters = []
)
{
$definitionSet = $this->getCustomFields($type, $group, $onlyInclude, $additionalFilters);
if (!$definitionSet)
{
return null;
}
return $definitionSet->filterEditable($set, $editMode);
}
public function getGlobalTemplateData(AbstractReply $reply = null)
{
$request = $this->request();
$jobRunTime = null;
if (!empty($this->options()->jobRunTrigger) && $this->options()->jobRunTrigger == 'activity')
{
// if activity based it is triggered by inclusion in the template - run time compared below
$jobRunTime = $this['job.runTime'];
}
$language = \XF::language();
$config = $this->config();
$cookieConfig = $config['cookie'];
$cookieConfig['secure'] = $request->isSecure() ? true : false;
$data = [
'versionId' => \XF::$versionId,
'version' => \XF::$version,
'app' => $this,
'request' => $request,
'uri' => $request->getRequestUri(),
'fullUri' => $request->getFullRequestUri(),
'time' => \XF::$time,
'timeDetails' => $language->getDayStartTimestamps(),
'debug' => \XF::$debugMode,
'development' => \XF::$developmentMode,
'designer' => $config['designer']['enabled'],
'visitor' => \XF::visitor(),
'session' => $this->session(),
'cookie' => $cookieConfig,
'enableRtnProtect' => $config['enableReverseTabnabbingProtection'],
'serviceWorkerPath' => $config['serviceWorkerPath'],
'language' => $language,
'style' => $this->templater()->getStyle(),
'isRtl' => $language->isRtl(),
'options' => $this->options(),
'reactions' => $this->get('reactions'),
'reactionsActive' => array_filter($this->get('reactions'), function(array $reaction)
{
return ($reaction['active'] === true);
}),
'addOns' => $this->container['addon.cache'],
'runJobs' => ($jobRunTime && $jobRunTime <= \XF::$time),
'simpleCache' => $this->simpleCache(),
'livePayments' => $config['enableLivePayments'],
'fullJs' => $config['development']['fullJs'],
'contactUrl' => $this->container['contactUrl'],
'privacyPolicyUrl' => $this->container['privacyPolicyUrl'],
'tosUrl' => $this->container['tosUrl'],
'homePageUrl' => $this->container['homePageUrl'],
'helpPageCount' => $this->container['helpPageCount'],
'uploadMaxFilesize' => $this->container['uploadMaxFilesize'],
'allowedVideoExtensions' => array_keys($this->container['inlineVideoTypes']),
'allowedAudioExtensions' => array_keys($this->container['inlineAudioTypes'])
];
if ($reply)
{
$replyData = [
'controller' => $reply->getControllerClass(),
'action' => $reply->getAction(),
'section' => $reply->getSectionContext(),
'containerKey' => $reply->getContainerKey(),
'contentKey' => $reply->getContentKey()
];
if ($reply instanceof \XF\Mvc\Reply\View)
{
$replyData['view'] = $reply->getViewClass();
$replyData['template'] = $reply->getTemplateName();
}
else if ($reply instanceof \XF\Mvc\Reply\Error || $reply->getResponseCode() >= 400)
{
$replyData['template'] = 'error';
}
else if ($reply instanceof \XF\Mvc\Reply\Message)
{
$replyData['template'] = 'message_page';
}
$data['reply'] = $replyData;
}
$this->fire('templater_global_data', [$this, &$data, $reply]);
return $data;
}
protected $updateCsrfCookie = false;
public function updateCsrfCookie($newValue)
{
$this->updateCsrfCookie = $newValue;
}
public function complete(Http\Response $response)
{
if (!$response->headerExists('Expires'))
{
$response->header('Expires', 'Thu, 19 Nov 1981 08:52:00 GMT');
}
if (!$response->headerExists('Cache-control'))
{
$response->header('Cache-control', 'private, no-cache, max-age=0');
}
if ($this->updateCsrfCookie)
{
$response->setCookie('csrf', $this->updateCsrfCookie, 0, null, false);
}
if ($this->container->isCached('db'))
{
$db = $this->db();
if ($db instanceof \XF\Db\ReplicationAdapterInterface && $db->isForcedToWriteServerExplicit())
{
$forceTime = $db->getForceToWriteServerLength();
if ($forceTime > 0)
{
$response->setCookie('dbWriteForced', time());
}
}
}
$this->fire('app_complete', [$this, &$response]);
}
public function finalOutputFilter(Http\Response $response)
{
if (\XF::$debugMode && $this->request()->get('_debug'))
{
$response->contentType('text/html', 'utf-8');
$response->body($this->debugger()->getDebugPageHtml());
}
$this->fire('app_final_output', [$this, &$response]);
return $response;
}
public function getErrorRoute($action, array $params = [], $responseType = 'html')
{
return $this->router()->getNewRouteMatch('XF:Error', $action, $params, $responseType);
}
public function renderPage($content, AbstractReply $reply, AbstractRenderer $renderer)
{
if ($reply instanceof Redirect)
{
return $content;
}
if ($renderer instanceof Mvc\Renderer\Html)
{
$content = strval($content);
$pageParams = $renderer->getTemplater()->pageParams;
return $this->renderPageHtml($content, $pageParams, $reply, $renderer);
}
else
{
return $content;
}
}
protected function renderPageHtml($content, array $params, AbstractReply $reply, AbstractRenderer $renderer)
{
return $content;
}
/**
* @param string $hash
*
* Generates the hash used for redirects, such as foo/bar/#redirect-anchor
*
* @return string
*/
public function getRedirectHash($hash)
{
return '__' . $hash;
}
public function getDynamicRedirect($fallbackUrl = null, $useReferrer = true)
{
$request = $this->request();
$redirect = $request->filter('_xfRedirect', 'str');
if (!$redirect && $useReferrer)
{
$redirect = $request->getServer('HTTP_X_AJAX_REFERER');
if (!$redirect)
{
$redirect = $request->getReferrer();
}
}
if ($redirect && preg_match('/./su', $redirect))
{
if (strpos($redirect, "\n") === false && strpos($redirect, "\r") === false)
{
$fullBasePath = $request->getFullBasePath();
$fullRedirect = $request->convertToAbsoluteUri($redirect);
$redirectParts = @parse_url($fullRedirect);
if ($redirectParts && !empty($redirectParts['host']))
{
$pageParts = @parse_url($fullBasePath);
if ($pageParts && !empty($pageParts['host']) && $pageParts['host'] == $redirectParts['host'])
{
return $fullRedirect;
}
}
}
}
if ($fallbackUrl === null)
{
$fallbackUrl = $this->router()->buildLink('index');
}
return $fallbackUrl;
}
public function getDynamicRedirectIfNot($notUrl, $fallbackUrl = null, $useReferrer = true)
{
$request = $this->request();
$redirect = $this->getDynamicRedirect($fallbackUrl, $useReferrer);
$notUrl = $request->convertToAbsoluteUri($notUrl);
if (strpos($redirect, $notUrl) === 0)
{
// the URL we can't redirect to is at the start
if ($fallbackUrl === false)
{
$fallbackUrl = $this->router()->buildLink('index');
}
return $request->convertToAbsoluteUri($fallbackUrl);
}
else
{
return $redirect;
}
}
public function applyExternalDataUrl($externalPath, $canonical = false)
{
$pathType = ($canonical ? 'canonical' : 'root-base');
return $this->applyExternalDataUrlPathed($externalPath, $pathType);
}
public function applyExternalDataUrlPathed($externalPath, $pathType)
{
$externalDataUrl = $this->config('externalDataUrl');
if ($externalDataUrl instanceof \Closure)
{
$url = $externalDataUrl($externalPath, $pathType);
}
else
{
$url = "$externalDataUrl/$externalPath";
}
/** @var \Closure $pather */
$pather = $this->container('request.pather');
return $pather($url, $pathType);
}
public function assertConfigExists()
{
if (!$this->container['config']['exists'])
{
echo 'Config.php does not exist.';
exit;
}
}
public function checkDebugMode()
{
$config = $this->container['config'];
if ($config['development']['enabled'])
{
$config['debug'] = true;
\XF::$developmentMode = true;
}
if ($config['debug'])
{
\XF::$debugMode = true;
@ini_set('display_errors', true);
}
}
public function checkDbWriteForced()
{
// reading this cookie directly as we do this before the DB is initialized and therefore
// we haven't actually fetched things from the registry
$cookieName = $this->container['config']['cookie']['prefix'] . 'dbWriteForced';
if (isset($_COOKIE[$cookieName]) && is_scalar($_COOKIE[$cookieName]))
{
$writeForced = intval($_COOKIE[$cookieName]);
}
else
{
$writeForced = 0;
}
if ($writeForced)
{
$db = $this->db();
if ($db instanceof \XF\Db\ReplicationAdapterInterface)
{
$forceTime = $db->getForceToWriteServerLength();
if ($forceTime > 0 && $writeForced + $forceTime >= time())
{
$db->forceToWriteServer('implicit');
}
}
}
}
public function setupAddOnComposerAutoload()
{
if (!$this->config('enableListeners'))
{
return;
}
/** @var \XF\AddOn\Manager $addOnManager */
$addOnManager = $this->container['addon.manager'];
$addOns = $this->container['addon.composer'];
foreach ($addOns AS $addOnId => $composerData)
{
if ($addOnId == 'XF' || !$composerData)
{
continue;
}
$addOnPath = $addOnManager->getAddOnPath($addOnId);
if (is_array($composerData))
{
\XF::registerComposerAutoloadData($addOnPath . \XF::$DS . $composerData['autoload_path'], $composerData);
}
else
{
\XF::registerComposerAutoloadDir($addOnPath . \XF::$DS . $composerData);
}
}
}
public function run()
{
$response = $this->start(true);
if (!($response instanceof Http\Response))
{
$dispatcher = $this->dispatcher();
$response = $dispatcher->run();
}
$this->complete($response);
$response = $this->finalOutputFilter($response);
return $response;
}
public function logException($e, $rollback = false, $messagePrefix = '')
{
$this->error()->logException($e, $rollback, $messagePrefix);
}
public function displayFatalExceptionMessage($e)
{
$this->error()->displayFatalExceptionMessage($e);
}
public function get($key)
{
return $this->container->offsetGet($key);
}
public function create($type, $key, array $params = [])
{
return $this->container->create($type, $key, $params);
}
/**
* @param mixed $key
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->container->offsetGet($key);
}
/**
* @param mixed $key
* @param mixed $value
*/
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
{
$this->container->offsetSet($key, $value);
}
/**
* @param mixed $key
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($key)
{
return $this->container->offsetExists($key);
}
/**
* @param mixed $key
*/
#[\ReturnTypeWillChange]
public function offsetUnset($key)
{
$this->container->offsetUnset($key);
}
/**
* @param mixed $key
*
* @return mixed
*/
public function __get($key)
{
return $this->container->offsetGet($key);
}
/**
* @param mixed $key
* @param mixed $value
*/
public function __set($key, $value)
{
$this->container->offsetSet($key, $value);
}
/**
* @param string|null
*
* @return mixed
*/
public function config($key = null)
{
$config = $this->container['config'];
if ($key)
{
return $config[$key] ?? null;
}
else
{
return $config;
}
}
/**
* @return \XF\Http\Request
*/
public function request()
{
return $this->container['request'];
}
/**
* @return \XF\InputFilterer
*/
public function inputFilterer()
{
return $this->container['inputFilterer'];
}
/**
* @return \XF\Http\Response
*/
public function response()
{
return $this->container['response'];
}
/**
* @return Dispatcher
*/
public function dispatcher()
{
return $this->container['dispatcher'];
}
/**
* @param string|null $type
*
* @return \XF\Mvc\Router
*/
public function router($type = null)
{
return $type ? $this->container['router.' . $type] : $this->container['router'];
}
/**
* @return \XF\Db\AbstractAdapter
*/
public function db()
{
return $this->container['db'];
}
/**
* @param string $context Context of cache to load from. Empty represents the global cache.
* @param bool $fallbackToGlobal If true and no specific cache is available, fallback to the global cache
*
* @return \Doctrine\Common\Cache\CacheProvider|null
*/
public function cache($context = '', $fallbackToGlobal = true)
{
$cache = $this->container->create('cache', $context);
if (!$cache && $fallbackToGlobal && strlen($context))
{
$cache = $this->container->create('cache', '');
}
return $cache;
}
/**
* @return PermissionCache
*/
public function permissionCache()
{
return $this->container['permission.cache'];
}
/**
* @return \XF\Permission\Builder
*/
public function permissionBuilder()
{
return $this->container['permission.builder'];
}
/**
* @return \XF\DataRegistry
*/
public function registry()
{
return $this->container['registry'];
}
/**
* @return \XF\SimpleCache
*/
public function simpleCache()
{
return $this->container['simpleCache'];
}
/**
* @return \ArrayObject
*/
public function options()
{
return $this->container['options'];
}
/**
* @return \XF\Mail\Mailer
*/
public function mailer()
{
return $this->container['mailer'];
}
/**
* @return \XF\Mail\Queue
*/
public function mailQueue()
{
return $this->container['mailer.queue'];
}
/**
* @return \XF\Mail\Templater
*/
public function mailTemplater()
{
return $this->container['mailer.templater'];
}
/**
* @return \League\Flysystem\MountManager
*/
public function fs()
{
return $this->container['fs'];
}
public function getContentTypeField($field)
{
$output = [];
foreach ($this->container['contentTypes'] AS $type => $fields)
{
if (isset($fields[$field]))
{
$output[$type] = $fields[$field];
}
}
return $output;
}
public function getContentTypeFieldValue($type, $field)
{
$types = $this->container['contentTypes'];
return $types[$type][$field] ?? null;
}
public function getContentTypePhraseName($type, $plural = false)
{
$types = $this->container['contentTypes'];
if (!isset($types[$type]))
{
return '';
}
$fields = $types[$type];
if ($plural)
{
if (isset($fields['phrase_plural']))
{
return $fields['phrase_plural'];
}
else if (isset($fields['phrase']))
{
return $fields['phrase'] . 's';
}
else
{
return "{$type}s";
}
}
else
{
return $fields['phrase'] ?? $type;
}
}
public function getContentTypePhrase($type, $plural = false)
{
$phraseName = $this->getContentTypePhraseName($type, $plural);
return strlen($phraseName) > 0 ? \XF::phrase($phraseName) : '';
}
public function getContentTypePhrases($plural = false, $withField = null)
{
$output = [];
foreach ($this->container['contentTypes'] AS $type => $fields)
{
if (!$withField || isset($fields[$withField]))
{
if ($plural)
{
if (isset($fields['phrase_plural']))
{
$phrase = $fields['phrase_plural'];
}
else if (isset($fields['phrase']))
{
$phrase = $fields['phrase'] . 's';
}
else
{
$phrase = "{$type}s";
}
}
else
{
$phrase = $fields['phrase'] ?? $type;
}
$output[$type] = \XF::phrase($phrase);
}
}
return $output;
}
/**
* @return \XF\Session\Session
*/
public function session()
{
return $this->container['session'];
}
/**
* @return Error
*/
public function error()
{
return $this->container['error'];
}
/**
* @return \XF\Mvc\Entity\Manager
*/
public function em()
{
return $this->container['em'];
}
public function find($entity, $id, $with = [])
{
return $this->em()->find($entity, $id, $with);
}
/**
* @param string $contentType
* @param int|array $contentId
* @param string|array $with
*
* @return null|Mvc\Entity\ArrayCollection|Mvc\Entity\Entity
*/
public function findByContentType($contentType, $contentId, $with = [])
{
$entity = $this->getContentTypeEntity($contentType);
if (is_array($contentId))
{
return $this->em()->findByIds($entity, $contentId, $with);
}
else
{
return $this->em()->find($entity, $contentId, $with);
}
}
/**
* @param string $contentType
* @param bool $throw
*
* @return string|null
*/
public function getContentTypeEntity($contentType, $throw = true)
{
$entity = $this->getContentTypeFieldValue($contentType, 'entity');
if (!$entity && $throw)
{
throw new \LogicException("Content type $contentType must define an 'entity' value");
}
return $entity;
}
/**
* @param string $identifier
*
* @return Mvc\Entity\Finder
*/
public function finder($identifier)
{
return $this->em()->getFinder($identifier);
}
/**
* @param string $identifier
*
* @return Mvc\Entity\Repository
*/
public function repository($identifier)
{
return $this->em()->getRepository($identifier);
}
/**
* @param integer $id
*
* @return \XF\Language
*/
public function language($id = 0)
{
return $this->container->create('language', $id);
}
/**
* Gets the effective language for the specified user. If the language is not usable by this person,
* it falls back to the default.
*
* @param Entity\User $user
*
* @return Language
*/
public function userLanguage(\XF\Entity\User $user)
{
$language = $this->language($user->language_id);
if (!$language->isUsable($user))
{
$language = $this->language(0);
}
return $language;
}
/**
* @param integer $id
*
* @return \XF\Style
*/
public function style($id = 0)
{
return $this->container->create('style', $id);
}
/**
* @param string $class
* @param Http\Request $request
*
* @return \XF\Mvc\Controller
*/
public function controller($class, Http\Request $request)
{
return $this->container->create('controller', $class, [
'request' => $request
]);
}
/**
* @return \XF\Job\Manager
*/
public function jobManager()
{
return $this->container['job.manager'];
}
/**
* @return \XF\Extension
*/
public function extension()
{
return $this->container['extension'];
}
/**
* Fires a code event for an extension point
*
* @param string $event
* @param array $args
* @param null|string $hint
*
* @return bool
*/
public function fire($event, array $args, $hint = null)
{
return $this->extension()->fire($event, $args, $hint);
}
/**
* Gets the callable class name for a dynamically extended class.
*
* @param string $class
* @param null|string $fakeBaseClass
* @return string
*
* @throws \Exception
*/
public function extendClass($class, $fakeBaseClass = null)
{
return $this->extension()->extendClass($class, $fakeBaseClass);
}
/**
* @return \XF\Search\Search
*/
public function search()
{
return $this->container['search'];
}
/**
* @return \XF\Str\Formatter
*/
public function stringFormatter()
{
return $this->container['string.formatter'];
}
/**
* @return \XF\SubContainer\BbCode
*/
public function bbCode()
{
return $this->container['bbCode'];
}
/**
* @return \XF\SubContainer\ApiDocs
*/
public function apiDocs()
{
return $this->container['apiDocs'];
}
/**
* @return \XF\Image\Manager
*/
public function imageManager()
{
return $this->container['imageManager'];
}
/**
* @return \XF\Logger
*/
public function logger()
{
return $this->container['logger'];
}
/**
* @return \XF\Debugger
*/
public function debugger()
{
return $this->container['debugger'];
}
/**
* @param string $class
* @param integer $jobId,
* @param array $params
*
* @return \XF\Job\AbstractJob
*/
public function job($class, $jobId, array $params = [])
{
$arguments = [$jobId, $params];
return $this->container->create('job', $class, $arguments);
}
/**
* @param string $class
* @param array|null $criteria
*
* @return \XF\Searcher\AbstractSearcher
*/
public function searcher($class, array $criteria = null)
{
return $this->container->create('searcher', $class, [$criteria]);
}
/**
* @param string $class
* @param array $setupData
*
* @return \XF\Filterer\AbstractFilterer
*/
public function filterer($class, array $setupData = [])
{
return $this->container->create('filterer', $class, [$setupData]);
}
/**
* @param string $class
* @param array $data
*
* @return \XF\Authentication\AbstractAuth
*/
public function auth($class, array $data = [])
{
if (!$class)
{
$class = $this->container['auth.default'];
}
return $this->container->create('auth', $class, [$data]);
}
/**
* @param string $type
* @param bool $throw Throw an exception on invalid type; if false, returns null instead
*
* @return \XF\ForumType\AbstractHandler|null
*/
public function forumType($type, $throw = true)
{
$forumType = $this->container->create('forumType', $type);
if (!$forumType && $throw)
{
throw new \InvalidArgumentException("Invalid forum type '$type'");
}
return $forumType;
}
/**
* @param string $type
* @param bool $throw Throw an exception on invalid type; if false, returns null instead
*
* @return \XF\ThreadType\AbstractHandler|null
*/
public function threadType($type, $throw = true)
{
$threadType = $this->container->create('threadType', $type);
if (!$threadType && $throw)
{
throw new \InvalidArgumentException("Invalid thread type '$type'");
}
return $threadType;
}
/**
* @param $class
* @param mixed ...$arguments
*
* @return mixed
*/
public function service($class, ...$arguments)
{
return $this->container->create('service', $class, $arguments);
}
/**
* @param string $class
* @param mixed ...$arguments
*
* @return mixed
*/
public function helper($class, ...$arguments)
{
return $this->container->create('helper', $class, $arguments);
}
/**
* @param string $class
*
* @return mixed Data container object
*/
public function data($class)
{
return $this->container->create('data', $class);
}
/**
* @param string $type
*
* @return \XF\Validator\AbstractValidator
*/
public function validator($type)
{
return $this->container->create('validator', $type);
}
/**
* @param array $columns
* @param array $existingValues
* @param bool $isUpdating
*
* @return \XF\Mvc\Entity\ArrayValidator
*/
public function arrayValidator(
array $columns,
array $existingValues = [],
bool $isUpdating = false
): \XF\Mvc\Entity\ArrayValidator
{
$valueFormatter = $this->get('em.valueFormatter');
$class = $this->extendClass('XF\Mvc\Entity\ArrayValidator');
return new $class($columns, $valueFormatter, $existingValues, $isUpdating);
}
/**
* @return \XF\Captcha\AbstractCaptcha|false
*/
public function captcha($class = null)
{
if ($class === null)
{
$class = $this->options()->captcha;
if (!$class)
{
return false;
}
}
$arguments = func_get_args();
unset($arguments[0]);
return $this->container->create('captcha', $class, $arguments);
}
/**
* @param $class
* @param $criteria
*
* @return \XF\Criteria\AbstractCriteria
*/
public function criteria($class, array $criteria)
{
$arguments = func_get_args();
unset($arguments[0]);
return $this->container->create('criteria', $class, $arguments);
}
/**
* @param string $class
*
* @return \XF\Notifier\AbstractNotifier
*/
public function notifier($class)
{
$arguments = func_get_args();
unset($arguments[0]);
return $this->container->create('notifier', $class, $arguments);
}
/**
* @return \XF\SubContainer\Spam
*/
public function spam()
{
return $this->container['spam'];
}
/**
* @return \XF\SubContainer\Http
*/
public function http()
{
return $this->container['http'];
}
/**
* @return \XF\SubContainer\OAuth
*/
public function oAuth()
{
return $this->container['oAuth'];
}
/**
* @return \XF\SubContainer\Proxy
*/
public function proxy()
{
return $this->container['proxy'];
}
/**
* @return \XF\SubContainer\Oembed
*/
public function oembed()
{
return $this->container['oembed'];
}
/**
* @return \XF\SubContainer\Bounce
*/
public function bounce()
{
return $this->container['bounce'];
}
/**
* @return \XF\SubContainer\Unsubscribe
*/
public function unsubscribe()
{
return $this->container['unsubscribe'];
}
/**
* @return \XF\SubContainer\Widget
*/
public function widget()
{
return $this->container['widget'];
}
/**
* @return \XF\SubContainer\Import
*/
public function import()
{
return $this->container['import'];
}
/**
* @return \XF\Sitemap\Builder
*/
public function sitemapBuilder()
{
return $this->container['sitemap.builder'];
}
/**
* @param string $type
* @param mixed $value
* @param null $errorKey Returned error type identifier
* @param array $options
*
* @return bool
*/
public function isValid($type, $value, &$errorKey = null, array $options = [])
{
$validator = $this->validator($type);
$validator->setOptions($options);
return $validator->isValid($value, $errorKey);
}
/**
* @param bool $inTransaction
*
* @return Mvc\FormAction
*/
public function formAction($inTransaction = true)
{
$formAction = new Mvc\FormAction();
if ($inTransaction)
{
$formAction->applyInTransaction($this->db());
}
return $formAction;
}
/**
* @param string $type
*
* @return \XF\Mvc\Renderer\AbstractRenderer
*/
public function renderer($type)
{
return $this->container->create('renderer', $type);
}
/**
* @return Templater
*/
public function templater()
{
return $this->container['templater'];
}
/**
* @return Compiler
*/
public function templateCompiler()
{
return $this->container['templateCompiler'];
}
/**
* @return CssWriter
*/
public function cssWriter()
{
return $this->container['cssWriter'];
}
/**
* @return DevJsResponse
*/
public function developmentJsResponse()
{
return $this->container['development.jsResponse'];
}
/**
* @return \XF\DevelopmentOutput
*/
public function developmentOutput()
{
return $this->container['development.output'];
}
/**
* @return \XF\DesignerOutput
*/
public function designerOutput()
{
return $this->container['designer.output'];
}
/**
* @return AddOn\Manager
*/
public function addOnManager()
{
return $this->container('addon.manager');
}
/**
* @return AddOn\DataManager
*/
public function addOnDataManager()
{
return $this->container('addon.dataManager');
}
/**
* @return Giphy\Api|null
*/
public function giphyApi()
{
return $this->container('giphy.api');
}
/**
* @param string|null $key
*
* @return \XF\Container|mixed
*/
public function container($key = null)
{
return $key === null ? $this->container : $this->container[$key];
}
public function __sleep()
{
throw new \LogicException('Instances of ' . __CLASS__ . ' cannot be serialized or unserialized');
}
public function __wakeup()
{
throw new \LogicException('Instances of ' . __CLASS__ . ' cannot be serialized or unserialized');
}
}