/**
* Unserialize
*
* @todo Allow integration with unserialize_callback_func
* @param string $serialized
* @return mixed
* @throws Exception\RuntimeException On unserialize error.
*/
public function unserialize($serialized)
{
if (! is_string($serialized) || ! preg_match('/^((s|i|d|b|a|O|C):|N;)/', $serialized)) {
$value = $serialized;
if (is_object($value)) {
$value = $value::class;
} elseif (! is_string($value)) {
$value = gettype($value);
}
throw new Exception\RuntimeException(sprintf(
'Serialized data must be a string containing serialized PHP code; received: %s',
$value
));
}
// If we have a serialized boolean false value, just return false;
// prevents the unserialize handler from creating an error.
if ($serialized === static::$serializedFalse) {
return false;
}
$errorLevel = E_NOTICE;
if (PHP_VERSION_ID >= 80300) {
$errorLevel = E_WARNING;
}
ErrorHandler::start($errorLevel);
// The second parameter to unserialize() is only available on PHP 7.0 or higher
$ret = PHP_MAJOR_VERSION >= 7
Arguments
"Serialized data must be a string containing serialized PHP code; received: "
$this->listeners[] = $events->attach('incrementItems.pre', [$this, 'onIncrementItemsPre'], $prePriority);
$this->listeners[] = $events->attach('decrementItem.pre', [$this, 'onDecrementItemPre'], $prePriority);
$this->listeners[] = $events->attach('decrementItems.pre', [$this, 'onDecrementItemsPre'], $prePriority);
// overwrite capabilities
$this->listeners[] = $events->attach('getCapabilities.post', [$this, 'onGetCapabilitiesPost'], $postPriority);
}
/**
* On read item post
*
* @return void
*/
public function onReadItemPost(PostEvent $event)
{
$result = $event->getResult();
if ($result !== null) {
$serializer = $this->getOptions()->getSerializer();
$result = $serializer->unserialize($result);
$event->setResult($result);
}
}
/**
* On read items post
*
* @return void
*/
public function onReadItemsPost(PostEvent $event)
{
$serializer = $this->getOptions()->getSerializer();
$result = $event->getResult();
foreach ($result as &$value) {
$value = $serializer->unserialize($value);
}
$event->setResult($result);
}
/**
}
if ($this->sharedManager) {
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
$listOfListenersByPriority[$priority][] = $listeners;
}
}
// Sort by priority in reverse order
krsort($listOfListenersByPriority);
// Initial value of stop propagation flag should be false
$event->stopPropagation(false);
// Execute listeners
$responses = new ResponseCollection();
foreach ($listOfListenersByPriority as $listOfListeners) {
foreach ($listOfListeners as $listeners) {
foreach ($listeners as $listener) {
$response = $listener($event);
$responses->push($response);
// If the event was asked to stop propagating, do so
if ($event->propagationIsStopped()) {
$responses->setStopped(true);
return $responses;
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && $callback($response)) {
$responses->setStopped(true);
return $responses;
}
}
}
}
return $responses;
}
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function triggerEvent(EventInterface $event)
{
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerEventUntil(callable $callback, EventInterface $event)
{
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function attach($eventName, callable $listener, $priority = 1)
{
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
get_debug_type($eventName),
* Trigger a pre event and return the event response collection
*
* @param string $eventName
* @return ResponseCollection All handler return values
*/
protected function triggerPre($eventName, ArrayObject $args)
{
return $this->getEventManager()->triggerEvent(new Event($eventName . '.pre', $this, $args));
}
/**
* Triggers the PostEvent and return the result value.
*
* @param string $eventName
* @return mixed
*/
protected function triggerPost($eventName, ArrayObject $args, mixed &$result)
{
$postEvent = new PostEvent($eventName . '.post', $this, $args, $result);
$eventRs = $this->getEventManager()->triggerEvent($postEvent);
return $eventRs->stopped()
? $eventRs->last()
: $postEvent->getResult();
}
/**
* Trigger an exception event
*
* If the ExceptionEvent has the flag "throwException" enabled throw the
* exception after trigger else return the result.
*
* @param string $eventName
* @throws Exception\ExceptionInterface
* @return mixed
*/
protected function triggerException($eventName, ArrayObject $args, mixed &$result, \Exception $exception)
{
$exceptionEvent = new ExceptionEvent($eventName . '.exception', $this, $args, $result, $exception);
$eventRs = $this->getEventManager()->triggerEvent($exceptionEvent);
}
if ($argn > 2) {
$args['casToken'] = &$casToken;
}
$args = new ArrayObject($args);
try {
$eventRs = $this->triggerPre(__FUNCTION__, $args);
if ($eventRs->stopped()) {
$result = $eventRs->last();
} elseif ($args->offsetExists('success') && $args->offsetExists('casToken')) {
$result = $this->internalGetItem($args['key'], $args['success'], $args['casToken']);
} elseif ($args->offsetExists('success')) {
$result = $this->internalGetItem($args['key'], $args['success']);
} else {
$result = $this->internalGetItem($args['key']);
}
return $this->triggerPost(__FUNCTION__, $args, $result);
} catch (\Exception $e) {
$result = null;
$success = false;
return $this->triggerException(__FUNCTION__, $args, $result, $e);
}
}
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
abstract protected function internalGetItem(&$normalizedKey, &$success = null, mixed &$casToken = null);
/**
* Get multiple items.
*
* @throws Exception\ExceptionInterface
* @triggers getItem.pre(PreEvent)
* @triggers getItem.post(PostEvent)
* @triggers getItem.exception(ExceptionEvent)
*/
public function getItem($key, &$success = null, &$casToken = null)
{
$options = $this->getOptions();
if ($options->getReadable() && $options->getClearStatCache()) {
$this->filesystem->clearStatCache();
}
$argn = func_num_args();
if ($argn > 2) {
return parent::getItem($key, $success, $casToken);
} elseif ($argn > 1) {
return parent::getItem($key, $success);
}
return parent::getItem($key);
}
/**
* Get multiple items.
*
* @param array $keys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
* @triggers getItems.pre(PreEvent)
* @triggers getItems.post(PostEvent)
* @triggers getItems.exception(ExceptionEvent)
*/
public function getItems(array $keys)
{
$options = $this->getOptions();
if ($options->getReadable() && $options->getClearStatCache()) {
$this->filesystem->clearStatCache();
}
return parent::getItems($keys);
}
/**
* Obtain the authentication to use with the EDS API from cache if it exists. If
* not, then generate a new one.
*
* @param bool $isInvalid whether or not the the current token is invalid
*
* @return string
*/
protected function getAuthenticationToken($isInvalid = false)
{
$token = null;
if ($this->ipAuth) {
return $token;
}
if ($isInvalid) {
$this->cache->setItem('edsAuthenticationToken', null);
}
$authTokenData = $this->cache->getItem('edsAuthenticationToken');
if (isset($authTokenData)) {
$currentToken = $authTokenData['token'] ?? '';
$expirationTime = $authTokenData['expiration'] ?? 0;
$this->debug(
'Cached Authentication data: '
. "$currentToken, expiration time: $expirationTime"
);
// Check to see if the token expiration time is greater than the current
// time. If the token is expired or within 5 minutes of expiring,
// generate a new one.
if (!empty($currentToken) && (time() <= ($expirationTime - (60 * 5)))) {
return $currentToken;
}
}
$username = $this->userName;
$password = $this->password;
$orgId = $this->orgId;
if (!empty($username) && !empty($password)) {
}
/**
* Perform a search and return record collection.
*
* @param AbstractQuery $query Search query
* @param int $offset Search offset
* @param int $limit Search limit
* @param ParamBag $params Search backend parameters
*
* @return \VuFindSearch\Response\RecordCollectionInterface
**/
public function search(
AbstractQuery $query,
$offset,
$limit,
ParamBag $params = null
) {
// process EDS API communication tokens.
$authenticationToken = $this->getAuthenticationToken();
$sessionToken = $this->getSessionToken();
$this->debug(
"Authentication Token: $authenticationToken, SessionToken: $sessionToken"
);
// create query parameters from VuFind data
$queryString = $query->getAllTerms();
$paramsStr = implode('&', null !== $params ? $params->request() : []);
$this->debug(
"Query: $queryString, Limit: $limit, Offset: $offset, "
. "Params: $paramsStr"
);
$baseParams = $this->getQueryBuilder()->build($query);
$paramsStr = implode('&', $baseParams->request());
$this->debug("BaseParams: $paramsStr ");
if (null !== $params) {
$baseParams->mergeWith($params);
}
$baseParams->set('resultsPerPage', $limit);
*
* @return CommandInterface Command instance for method chaining
*/
public function execute(BackendInterface $backend): CommandInterface
{
$this->validateBackend($backend);
if (
!($backend instanceof $this->interface)
|| !method_exists($this->interface, $this->method)
) {
throw new BackendException(
"$this->backendId does not support $this->method()"
);
}
$args = $this->getArguments();
if ($backend instanceof ExtraRequestDetailsInterface) {
$backend->resetExtraRequestDetails();
}
$this->finalizeExecution(
call_user_func([$backend, $this->method], ...$args)
);
if ($backend instanceof ExtraRequestDetailsInterface) {
$this->extraRequestDetails = $backend->getExtraRequestDetails();
}
return $this;
}
}
*
* @return CommandInterface Command instance for method chaining
*/
public function execute(BackendInterface $backend): CommandInterface
{
$this->validateBackend($backend);
if (
!($backend instanceof $this->interface)
|| !method_exists($this->interface, $this->method)
) {
throw new BackendException(
"$this->backendId does not support $this->method()"
);
}
$args = $this->getArguments();
if ($backend instanceof ExtraRequestDetailsInterface) {
$backend->resetExtraRequestDetails();
}
$this->finalizeExecution(
call_user_func([$backend, $this->method], ...$args)
);
if ($backend instanceof ExtraRequestDetailsInterface) {
$this->extraRequestDetails = $backend->getExtraRequestDetails();
}
return $this;
}
}
}
/**
* Invoke a command.
*
* @param CommandInterface $command Command
*
* @return CommandInterface
*/
public function invoke(CommandInterface $command)
{
// The backend instance is no longer added as an event parameter.
// All other legacy event parameters are accessible via the command object.
$args = ['command' => $command];
$backend = $this->resolve($command->getTargetIdentifier(), $args);
$this->triggerPre($this, $args);
try {
$command->execute($backend);
} catch (BackendException $e) {
$args['error'] = $e;
$this->triggerError($this, $args);
throw $e;
}
$this->triggerPost($this, $args);
return $command;
}
/**
* Resolve a backend.
*
* @param string $backendId Backend name
* @param array|ArrayAccess $args Service function arguments
*
* @return BackendInterface
*
* @throws Exception\RuntimeException Unable to resolve backend
*/
/**
* Support method for performAndProcessSearch -- perform a search based on the
* parameters passed to the object.
*
* @return void
*/
protected function performSearch()
{
$query = $this->getParams()->getQuery();
$limit = $this->getParams()->getLimit();
$offset = $this->getStartRecord() - 1;
$params = $this->getParams()->getBackendParameters();
$command = new SearchCommand(
$this->backendId,
$query,
$offset,
$limit,
$params
);
$collection = $this->getSearchService()->invoke($command)
->getResult();
if (null != $collection) {
$this->responseFacets = $collection->getFacets();
$this->resultTotal = $collection->getTotal();
// Add fake date facets if flagged earlier; this is necessary in order
// to display the date range facet control in the interface.
$dateFacets = $this->getParams()->getDateFacetSettings();
if (!empty($dateFacets)) {
foreach ($dateFacets as $dateFacet) {
$this->responseFacets[$dateFacet] = [''];
}
}
// Construct record drivers for all the items in the response:
$this->results = $collection->getRecords();
}
}
/**
}
/**
* Actually execute the search.
*
* @return void
*/
public function performAndProcessSearch()
{
// Initialize variables to defaults (to ensure they don't stay null
// and cause unnecessary repeat processing):
// The value of -1 indicates that resultTotal is not available.
$this->resultTotal = -1;
$this->results = [];
$this->suggestions = [];
$this->errors = [];
// Run the search:
$this->startQueryTimer();
$this->performSearch();
$this->stopQueryTimer();
}
/**
* Returns the stored list of facets for the last search
*
* @param array $filter Array of field => on-screen description listing
* all of the desired facet fields; set to null to get all configured values.
*
* @return array Facets data arrays
*/
abstract public function getFacetList($filter = null);
/**
* Abstract support method for performAndProcessSearch -- perform a search based
* on the parameters passed to the object. This method is responsible for
* filling in all of the key class properties: results, resultTotal, etc.
*
* @return void
*/
$params->setLastView($lastView);
$params->initFromRequest($request);
if (is_callable($setupCallback)) {
$setupCallback($this, $params, $runningSearchId);
}
// Trigger the "configuration done" event.
$this->getEventManager()->trigger(
self::EVENT_CONFIGURED,
$this,
compact('params', 'request', 'runningSearchId')
);
// Attempt to perform the search; if there is a problem, inspect any Solr
// exceptions to see if we should communicate to the user about them.
try {
// Explicitly execute search within controller -- this allows us to
// catch exceptions more reliably:
$results->performAndProcessSearch();
} catch (\VuFindSearch\Backend\Exception\BackendException $e) {
if ($e->hasTag(ErrorListener::TAG_PARSER_ERROR)) {
// We need to create and process an "empty results" object to
// ensure that recommendation modules and templates behave
// properly when displaying the error message.
$results = $this->resultsManager->get('EmptySet');
$results->setParams($params);
$results->performAndProcessSearch();
} else {
throw $e;
}
}
// Trigger the "search completed" event.
$this->getEventManager()->trigger(
self::EVENT_COMPLETE,
$this,
compact('results', 'runningSearchId')
);
$view->multiFacetsSelection = (bool)($config->Results_Settings->multiFacetsSelection ?? false);
$extraErrors = [];
// Handle saved search requests:
$savedId = $this->params()->fromQuery('saved', false);
if ($savedId !== false) {
return $this->redirectToSavedSearch($savedId);
}
$runner = $this->getService(\VuFind\Search\SearchRunner::class);
// Send both GET and POST variables to search class:
$request = $this->getRequest()->getQuery()->toArray()
+ $this->getRequest()->getPost()->toArray();
$view->request = $request;
$lastView = $this->getSearchMemory()
->retrieveLastSetting($this->searchClassId, 'view');
try {
$view->results = $results = $runner->run(
$request,
$this->searchClassId,
$setupCallback ?: $this->getSearchSetupCallback(),
$lastView
);
} catch (\VuFindSearch\Backend\Exception\DeepPagingException $e) {
return $this->redirectToLegalSearchPage($request, $e->getLegalPage());
}
$view->params = $params = $results->getParams();
// For page parameter being out of results list, we want to redirect to correct page
$page = $params->getPage();
$totalResults = $results->getResultTotal();
$limit = $params->getLimit();
$lastPage = $limit ? ceil($totalResults / $limit) : 1;
if ($totalResults > 0 && $page > $lastPage) {
$queryParams = $request;
$queryParams['page'] = $lastPage;
return $this->redirect()->toRoute('search-results', [], [ 'query' => $queryParams ]);
}
throw new \Exception('Unrecoverable deep paging error.');
}
$request['page'] = $page;
$this->flashMessenger()->addErrorMessage(
[
'msg' => 'deep_paging_failure',
'tokens' => ['%%page%%' => $page],
]
);
return $this->redirect()->toUrl('?' . http_build_query($request));
}
/**
* Send search results to results view
*
* @return Response|ViewModel
*/
public function resultsAction()
{
return $this->getSearchResultsView();
}
/**
* Support method for getSearchResultsView() -- return the search results
* reformatted as an RSS feed.
*
* @param $view ViewModel View model
*
* @return Response
*/
protected function getRssSearchResponse(ViewModel $view): Response
{
// Build the RSS feed:
$feedHelper = $this->getViewRenderer()->plugin('resultfeed');
$feed = $feedHelper($view->results);
$writer = new \Laminas\Feed\Writer\Renderer\Feed\Rss($feed);
$writer->render();
// Apply XSLT if we can find a relevant file:
$themeInfo = $this->getService(\VuFindTheme\ThemeInfo::class);
$view = parent::advancedAction();
// Set up facet information:
$view->limiterList = $this->processAdvancedFacets(
$this->getAdvancedFacets(),
$view->saved
);
$view->expanderList = $this->processAdvancedExpanders($view->saved);
$view->searchModes = $this->processAdvancedSearchModes($view->saved);
$view->dateRangeLimit = $this->processPublicationDateRange($view->saved);
return $view;
}
/**
* Search action -- call standard results action
*
* @return mixed
*/
public function searchAction()
{
return $this->resultsAction();
}
/**
* Return a Search Results object containing advanced facet information. This
* data may come from the cache.
*
* @return array
*/
protected function getAdvancedFacets()
{
// VuFind facets are what the EDS API calls limiters. Available limiters
// are returned with a call to the EDS API Info method and are cached.
// Since they are obtained from a separate call, there is no need to call
// search.
// Check if we have facet results stored in session. Build them if we don't.
// pull them from the session cache
$results = $this->getResultsManager()->get('EDS');
$params = $results->getParams();
$options = $params->getOptions();
*/
public function onDispatch(MvcEvent $e)
{
$routeMatch = $e->getRouteMatch();
if (! $routeMatch) {
/**
* @todo Determine requirements for when route match is missing.
* Potentially allow pulling directly from request metadata?
*/
throw new DomainException('Missing route matches; unsure how to retrieve action');
}
$action = $routeMatch->getParam('action', 'not-found');
$method = static::getMethodFromAction($action);
if (! method_exists($this, $method)) {
$method = 'notFoundAction';
}
$actionResponse = $this->$method();
$e->setResult($actionResponse);
return $actionResponse;
}
}
}
if ($this->sharedManager) {
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
$listOfListenersByPriority[$priority][] = $listeners;
}
}
// Sort by priority in reverse order
krsort($listOfListenersByPriority);
// Initial value of stop propagation flag should be false
$event->stopPropagation(false);
// Execute listeners
$responses = new ResponseCollection();
foreach ($listOfListenersByPriority as $listOfListeners) {
foreach ($listOfListeners as $listeners) {
foreach ($listeners as $listener) {
$response = $listener($event);
$responses->push($response);
// If the event was asked to stop propagating, do so
if ($event->propagationIsStopped()) {
$responses->setStopped(true);
return $responses;
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && $callback($response)) {
$responses->setStopped(true);
return $responses;
}
}
}
}
return $responses;
}
$event->setParams($argv);
}
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function triggerEvent(EventInterface $event)
{
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerEventUntil(callable $callback, EventInterface $event)
{
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function attach($eventName, callable $listener, $priority = 1)
{
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
get_debug_type($eventName),
));
}
$this->events[$eventName][(int) $priority][0][] = $listener;
return $listener;
}
/**
* @events dispatch.pre, dispatch.post
* @param Request $request
* @param null|Response $response
* @return Response|mixed
*/
public function dispatch(Request $request, Response $response = null)
{
$this->request = $request;
if (! $response) {
$response = new HttpResponse();
}
$this->response = $response;
$e = $this->getEvent();
$e->setName(MvcEvent::EVENT_DISPATCH);
$e->setRequest($request);
$e->setResponse($response);
$e->setTarget($this);
$result = $this->getEventManager()->triggerEventUntil(static fn($test): bool => $test instanceof Response, $e);
if ($result->stopped()) {
return $result->last();
}
return $e->getResult();
}
/**
* Get request object
*
* @return Request
*/
public function getRequest()
{
if (! $this->request) {
$this->request = new HttpRequest();
}
return $this->request;
);
return $this->complete($return, $e);
} catch (Throwable $exception) {
$return = $this->marshalBadControllerEvent($controllerName, $e, $application, $exception);
return $this->complete($return, $e);
} catch (Exception $exception) { // @TODO clean up once PHP 7 requirement is enforced
$return = $this->marshalBadControllerEvent($controllerName, $e, $application, $exception);
return $this->complete($return, $e);
}
if ($controller instanceof InjectApplicationEventInterface) {
$controller->setEvent($e);
}
$request = $e->getRequest();
$response = $application->getResponse();
$caughtException = null;
try {
$return = $controller->dispatch($request, $response);
} catch (Throwable $ex) {
$caughtException = $ex;
} catch (Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced
$caughtException = $ex;
}
if ($caughtException !== null) {
$e->setName(MvcEvent::EVENT_DISPATCH_ERROR);
$e->setError($application::ERROR_EXCEPTION);
$e->setController($controllerName);
$e->setControllerClass($controller::class);
$e->setParam('exception', $caughtException);
$return = $application->getEventManager()->triggerEvent($e)->last();
if (! $return) {
$return = $e->getResult();
}
}
return $this->complete($return, $e);
}
if ($this->sharedManager) {
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
$listOfListenersByPriority[$priority][] = $listeners;
}
}
// Sort by priority in reverse order
krsort($listOfListenersByPriority);
// Initial value of stop propagation flag should be false
$event->stopPropagation(false);
// Execute listeners
$responses = new ResponseCollection();
foreach ($listOfListenersByPriority as $listOfListeners) {
foreach ($listOfListeners as $listeners) {
foreach ($listeners as $listener) {
$response = $listener($event);
$responses->push($response);
// If the event was asked to stop propagating, do so
if ($event->propagationIsStopped()) {
$responses->setStopped(true);
return $responses;
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && $callback($response)) {
$responses->setStopped(true);
return $responses;
}
}
}
}
return $responses;
}
$event->setParams($argv);
}
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function triggerEvent(EventInterface $event)
{
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerEventUntil(callable $callback, EventInterface $event)
{
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function attach($eventName, callable $listener, $priority = 1)
{
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
get_debug_type($eventName),
));
}
$this->events[$eventName][(int) $priority][0][] = $listener;
return $listener;
}
/**
$response = $result->last();
if ($response instanceof ResponseInterface) {
$event->setName(MvcEvent::EVENT_FINISH);
$event->setTarget($this);
$event->setResponse($response);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$this->response = $response;
return $this;
}
}
if ($event->getError()) {
return $this->completeRequest($event);
}
// Trigger dispatch event
$event->setName(MvcEvent::EVENT_DISPATCH);
$event->stopPropagation(false); // Clear before triggering
$result = $events->triggerEventUntil($shortCircuit, $event);
// Complete response
$response = $result->last();
if ($response instanceof ResponseInterface) {
$event->setName(MvcEvent::EVENT_FINISH);
$event->setTarget($this);
$event->setResponse($response);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$this->response = $response;
return $this;
}
$response = $this->response;
$event->setResponse($response);
return $this->completeRequest($event);
}
/**
* Complete the request
$vufindProfiler = getenv('VUFIND_PROFILER_XHPROF');
if (!empty($vufindProfiler)) {
include __DIR__ . '/../module/VuFind/functions/profiler.php';
enableVuFindProfiling($vufindProfiler);
}
// Run the application!
$app = include __DIR__ . '/../config/application.php';
if (PHP_SAPI === 'cli') {
return $app->getServiceManager()
->get(\VuFindConsole\ConsoleRunner::class)->run();
} else {
// Setup remote code coverage if enabled:
if (getenv('VUFIND_CODE_COVERAGE')) {
$modules = $app->getServiceManager()
->get(\Laminas\ModuleManager\ModuleManager::class)->getModules();
include __DIR__ . '/../module/VuFind/functions/codecoverage.php';
setupVuFindRemoteCodeCoverage($modules);
}
$app->run();
}