/** * @dataProvider getPathPartData */ public function testGetPartsFromPath($path, $expected, $expectedException) { if (!empty($expectedException)) { $this->setExpectedException($expectedException); } $this->assertEquals($expected, AgaviArrayPathDefinition::getPartsFromPath($path)); }
/** * Checks if a parameter exists * in the array * @param string $name * @return mixed */ protected function hasParameter($name) { if (isset($this->parameters[$name]) || array_key_exists($name, $this->parameters)) { return true; } try { return AgaviArrayPathDefinition::hasValue($name, $this->parameters); } catch (InvalidArgumentException $e) { return false; } }
/** * Exports a value back into the request. * * Exports data into the request at the index given in the parameter * 'export'. If there is no such parameter, then the method returns * without exporting. * * Similar to getData() you should always use export() to submit data to * the request because it pays attention to paths and otherwise you could * overwrite stuff you don't want to. * * @param mixed The value to be exported. * @param string An optional name which should be used for exporting * instead of the export parameter. * * @author Dominik del Bondio <*****@*****.**> * @since 0.11.0 */ protected function export($value, $name = null) { if ($name === null) { $name = $this->getParameter('export'); } if (!is_string($name) || $name === '') { return; } $paramType = $this->getParameter('source'); $array =& $this->validationParameters->getAll($paramType); $currentParts = $this->curBase->getParts(); if (count($currentParts) > 0 && strpos($name, '%') !== false) { // this is a validator which actually has a base (<arguments base="xx">) set // and the export name contains sprintf syntax $name = vsprintf($name, $currentParts); } // CAUTION // we had a feature here during development that would allow [] at the end to append values to an array // that would, however, mean that we have to cast the value to an array, and, either way, a user would be able to manipulate the keys // example: we export to foo[], and the user supplies ?foo[28] in the URL. that means our export will be in foo[29]. foo[28] will be removed by the validation, but the keys are still potentially harmful // that's why we decided to remove this again $cp = new AgaviVirtualArrayPath($name); $cp->setValue($array, $value); if ($this->parentContainer !== null) { // make sure the parameter doesn't get removed by the validation manager if (is_array($value)) { // for arrays all child elements need to be marked as not processed foreach (AgaviArrayPathDefinition::getFlatKeyNames($value) as $keyName) { $this->parentContainer->addArgumentResult(new AgaviValidationArgument($cp->pushRetNew($keyName)->__toString(), $this->getParameter('source')), AgaviValidator::SUCCESS, $this); } } $this->parentContainer->addArgumentResult(new AgaviValidationArgument($cp->__toString(), $this->getParameter('source')), AgaviValidator::SUCCESS, $this); } }
/** * Corrects the order of $_FILES for arrays of files. * The cleaned up array of AgaviUploadedFile objects is put into $this->files. * * @param array Array of indices used during recursion, initially empty. * * @author David Zülke <*****@*****.**> * @since 0.11.0 */ protected function fixFilesArray(&$input = array(), $index = array()) { $fromIndex = $index; if (count($fromIndex) > 0) { $first = array_shift($fromIndex); array_unshift($fromIndex, $first, 'error'); } else { // first call $input = $this->files; $this->files = array(); } $sub = AgaviArrayPathDefinition::getValue($fromIndex, $input); $theIndices = array(); foreach (array('name', 'type', 'size', 'tmp_name', 'error', 'is_uploaded_file') as $name) { $theIndex = $fromIndex; $first = array_shift($theIndex); array_shift($theIndex); array_unshift($theIndex, $first, $name); $theIndices[$name] = $theIndex; } if (is_array($sub)) { foreach ($sub as $key => $value) { $toIndex = array_merge($index, array($key)); if (is_array($value)) { $this->fixFilesArray($input, $toIndex); } else { $data = new $this->uploadedFileClass(); foreach ($theIndices as $name => $theIndex) { $data[$name] = AgaviArrayPathDefinition::getValue(array_merge($theIndex, array($key)), $input, true); } AgaviArrayPathDefinition::setValue($toIndex, $this->files, $data); } } } else { $data = new $this->uploadedFileClass(); foreach ($theIndices as $name => $theIndex) { $data[$name] = AgaviArrayPathDefinition::getValue($theIndex, $input, true); } AgaviArrayPathDefinition::setValue($index, $this->files, $data); } }
/** * Retrieve an array of flattened file names. This means when a file is an * array you wont get the name of the file in the result but instead all child * keys appended to the name (like foo[0],foo[1][0], ...). * * @return array An indexed array of file names. * * @author David Zülke <*****@*****.**> * @since 0.11.0 */ public function getFlatFileNames() { return AgaviArrayPathDefinition::getFlatKeyNames($this->files); }
/** * Render the presentation and return the result. * * @param AgaviTemplateLayer The template layer to render. * @param array The template variables. * @param array The slots. * @param array Associative array of additional assigns. * * @return string A rendered result. * * @author David Zülke <*****@*****.**> * @since 1.1.0 */ public function render(AgaviTemplateLayer $layer, array &$attributes = array(), array &$slots = array(), array &$moreAssigns = array()) { if ($this->getParameter('envelope', true)) { if (!$moreAssigns['inner'] instanceof DOMDocument) { // plain text, load it as a document try { $inner = $this->loadDomDocumentXml($moreAssigns['inner']); } catch (DOMException $e) { throw new AgaviRenderException(sprintf("Unable to load input document for layer '%s'.\n\n%s", $layer->getName(), $e->getMessage())); } } else { $inner = $moreAssigns['inner']; } // construct envelope $doc = new DOMDocument(); $doc->appendChild($doc->createElementNS(self::ENVELOPE_XMLNS, 'envelope')); // inner content container $doc->documentElement->appendChild($innerWrapper = $doc->createElementNS(self::ENVELOPE_XMLNS, 'inner')); $innerWrapper->appendChild($doc->importNode($inner->documentElement, true)); // slots container $slotsWrapper = $doc->createElementNS(self::ENVELOPE_XMLNS, 'slots'); $doc->documentElement->appendChild($slotsWrapper); // flatten slots, iterate and wrap them each $flattenedSlots = AgaviArrayPathDefinition::flatten($slots); foreach ($flattenedSlots as $slotName => $slotContent) { if (!$slotContent instanceof DOMDocument) { try { $slot = $this->loadDomDocumentXml($slotContent); } catch (Exception $e) { throw new AgaviRenderException(sprintf("Unable to load contents for slot '%s'.\n\n%s", $slotName, $e->getMessage())); } } else { $slot = $slotContent; } $slotWrapper = $doc->createElementNS(self::ENVELOPE_XMLNS, 'slot'); $slotWrapper->setAttribute('name', $slotName); $slotWrapper->appendChild($doc->importNode($slot->documentElement, true)); $slotsWrapper->appendChild($slotWrapper); } } else { if (!$moreAssigns['inner'] instanceof DOMDocument) { // plain text, load it as a document $doc = $this->loadDomDocumentXml($moreAssigns['inner']); } else { $doc = $moreAssigns['inner']; } // This will pretty much never work, so we're not doing it. Users must enable the envelope feature to use slots. // Warning: XSLTProcessor::transformToXml() [xsltprocessor.transformtoxml]: Cannot create XPath expression (string contains both quote and double-quotes) // $flattenedSlots = AgaviArrayPathDefinition::flatten($slots); // foreach($flattenedSlots as $slotName => $slotContent) { // if($slotContent instanceof DOMDocument) { // $slotContent = $slotContent->saveXML(); // } // $xsl->setParameter('', 'slot:' . $slotName, addslashes($slotContent)); // } } try { $xslt = $this->loadDomDocument($layer->getResourceStreamIdentifier()); } catch (DOMException $e) { throw new AgaviRenderException(sprintf("Unable to load template '%s'.\n\n%s", $layer->getResourceStreamIdentifier(), $e->getMessage())); } $xsl = new XSLTProcessor(); $xsl->importStylesheet($xslt); foreach ($attributes as $name => $attribute) { if (is_scalar($attribute) || is_object($attribute) && method_exists($attribute, '__toString')) { $xsl->setParameter('', $name, $attribute); } } return $xsl->transformToXML($doc); }
/** * Retrieves the value for a given entry from the source. * * @param array An array with the name parts for the entry. * * @return mixed The value. * * @author Dominik del Bondio <*****@*****.**> * @since 0.11.0 */ public function getSource(array $parts) { return AgaviArrayPathDefinition::getValue($parts, $this->data); }
/** * Remove an attribute. * * @param string An attribute name. * @param string An attribute namespace. * * @return mixed An attribute value, if the attribute was removed, * otherwise null. * * @author Sean Kerr <*****@*****.**> * @since 0.9.0 */ public function &removeAttribute($name, $ns = null) { if ($ns === null) { $ns = $this->defaultNamespace; } $retval = null; if (isset($this->attributes[$ns])) { if (isset($this->attributes[$ns][$name]) || array_key_exists($name, $this->attributes[$ns])) { $retval =& $this->attributes[$ns][$name]; unset($this->attributes[$ns][$name]); } else { try { $retval =& AgaviArrayPathDefinition::unsetValue($name, $this->attributes[$ns]); } catch (InvalidArgumentException $e) { } } } return $retval; }
/** * Execute this filter. * * @param AgaviFilterChain The filter chain. * @param AgaviExecutionContainer The current execution container. * * @throws <b>AgaviInitializationException</b> If an error occurs during * View initialization. * @throws <b>AgaviViewException</b> If an error occurs while * executing the View. * * @author David Zülke <*****@*****.**> * @author Felix Gilcher <*****@*****.**> * @author Sean Kerr <*****@*****.**> * @since 0.9.0 */ public function execute(AgaviFilterChain $filterChain, AgaviExecutionContainer $container) { // $lm = $this->context->getLoggerManager(); // get the context, controller and validator manager $controller = $this->context->getController(); // get the current action information $actionName = $container->getActionName(); $moduleName = $container->getModuleName(); // the action instance $actionInstance = $container->getActionInstance(); $request = $this->context->getRequest(); $isCacheable = false; $cachingDotXml = AgaviToolkit::evaluateModuleDirective($moduleName, 'agavi.cache.path', array('moduleName' => $moduleName, 'actionName' => $actionName)); if ($this->getParameter('enable_caching', true) && is_readable($cachingDotXml)) { // $lm->log('Caching enabled, configuration file found, loading...'); // no _once please! include AgaviConfigCache::checkConfig($cachingDotXml, $this->context->getName()); } $isActionCached = false; if ($isCacheable) { try { $groups = $this->determineGroups($config['groups'], $container); $actionGroups = array_merge($groups, array(self::ACTION_CACHE_ID)); } catch (AgaviUncacheableException $e) { // a group callback threw an exception. that means we're not allowed t cache $isCacheable = false; } if ($isCacheable) { // this is not wrapped in the try/catch block above as it might throw an exception itself $isActionCached = $this->checkCache(array_merge($groups, array(self::ACTION_CACHE_ID)), $config['lifetime']); if (!$isActionCached) { // cacheable, but action is not cached. notify our callback so it can prevent the stampede that follows $this->startedCacheCreationCallback(self::CACHE_CALLBACK_ACTION_NOT_CACHED, $actionGroups, $config, $container); } } } else { // $lm->log('Action is not cacheable!'); } if ($isActionCached) { // $lm->log('Action is cached, loading...'); // cache/dir/4-8-15-16-23-42 contains the action cache try { $actionCache = $this->readCache($actionGroups); // and restore action attributes $actionInstance->setAttributes($actionCache['action_attributes']); } catch (AgaviException $e) { // cacheable, but action is not cached. notify our callback so it can prevent the stampede that follows $this->startedCacheCreationCallback(self::CACHE_CALLBACK_ACTION_CACHE_GONE, $actionGroups, $config, $container); $isActionCached = false; } } $isViewCached = false; $rememberTheView = null; while (true) { if (!$isActionCached) { $actionCache = array(); // $lm->log('Action not cached, executing...'); // execute the Action and get the View to execute list($actionCache['view_module'], $actionCache['view_name']) = $container->runAction(); // check if we've just run the action again after a previous cache read revealed that the view is not cached for this output type and we need to go back to square one due to the lack of action attribute caching configuration... // if yes: is the view module/name that we got just now different from what was in the cache? if (isset($rememberTheView) && $actionCache != $rememberTheView) { // yup. clear it! $ourClass = get_class($this); $ourClass::clearCache($groups); } // check if the returned view is cacheable if ($isCacheable && is_array($config['views']) && !in_array(array('module' => $actionCache['view_module'], 'name' => $actionCache['view_name']), $config['views'], true)) { $isCacheable = false; $this->abortedCacheCreationCallback(self::CACHE_CALLBACK_VIEW_NOT_CACHEABLE, $actionGroups, $config, $container); // so that view is not cacheable? okay then: // check if we've just run the action again after a previous cache read revealed that the view is not cached for this output type and we need to go back to square one due to the lack of action attribute caching configuration... // 'cause then we need to flush all those existing caches - obviously, that data is stale now, as we learned, since we are not allowed to cache anymore for the view that was returned now if (isset($rememberTheView)) { // yup. clear it! $ourClass = get_class($this); $ourClass::clearCache($groups); } // $lm->log('Returned View is not cleared for caching, setting cacheable status to false.'); } else { // $lm->log('Returned View is cleared for caching, proceeding...'); } $actionAttributes = $actionInstance->getAttributes(); } // clear the response $response = $container->getResponse(); $response->clear(); // clear any forward set, it's ze view's job $container->clearNext(); if ($actionCache['view_name'] !== AgaviView::NONE) { $container->setViewModuleName($actionCache['view_module']); $container->setViewName($actionCache['view_name']); $key = $request->toggleLock(); try { $viewInstance = $container->getViewInstance(); } catch (Exception $e) { // we caught an exception... unlock the request and rethrow! $request->toggleLock($key); throw $e; } $request->toggleLock($key); $outputType = $container->getOutputType()->getName(); if ($isCacheable) { if (isset($config['output_types'][$otConfig = $outputType]) || isset($config['output_types'][$otConfig = '*'])) { $otConfig = $config['output_types'][$otConfig]; $viewGroups = array_merge($groups, array($outputType)); if ($isActionCached) { $isViewCached = $this->checkCache($viewGroups, $config['lifetime']); if (!$isViewCached) { // cacheable, but view is not cached. notify our callback so it can prevent the stampede that follows $this->startedCacheCreationCallback(self::CACHE_CALLBACK_VIEW_NOT_CACHED, $viewGroups, $config, $container); } } } else { $this->abortedCacheCreationCallback(self::CACHE_CALLBACK_OUTPUT_TYPE_NOT_CACHEABLE, $actionGroups, $config, $container); $isCacheable = false; } } if ($isViewCached) { // $lm->log('View is cached, loading...'); try { $viewCache = $this->readCache($viewGroups); } catch (AgaviException $e) { $this->startedCacheCreationCallback(self::CACHE_CALLBACK_VIEW_CACHE_GONE, $viewGroups, $config, $container); $isViewCached = false; } } if (!$isViewCached) { // view not cached // has the cache config a list of action attributes? if ($isActionCached && !$config['action_attributes']) { // no. that means we must run the action again! $isActionCached = false; if ($isCacheable) { // notify our callback so it can remove the lock that's on the view // but only if we're still marked as cacheable (if not, then that means the OT is not cacheable, so there wouldn't be a $viewGroups) $this->abortedCacheCreationCallback(self::CACHE_CALLBACK_ACTION_CACHE_USELESS, $viewGroups, $config, $container); } // notify our callback so it can prevent the stampede that follows $this->startedCacheCreationCallback(self::CACHE_CALLBACK_ACTION_CACHE_USELESS, $actionGroups, $config, $container); // but remember the view info, just in case it differs if we run the action again now $rememberTheView = array('view_module' => $actionCache['view_module'], 'view_name' => $actionCache['view_name']); continue; } $viewCache = array(); $viewCache['next'] = $this->executeView($container); } if ($viewCache['next'] instanceof AgaviExecutionContainer) { // $lm->log('Forwarding request, skipping rendering...'); $container->setNext($viewCache['next']); } else { $output = array(); $nextOutput = null; if ($isViewCached) { $layers = $viewCache['layers']; $response = $viewCache['response']; $container->setResponse($response); foreach ($viewCache['template_variables'] as $name => $value) { $viewInstance->setAttribute($name, $value); } foreach ($viewCache['request_attributes'] as $requestAttribute) { $request->setAttribute($requestAttribute['name'], $requestAttribute['value'], $requestAttribute['namespace']); } foreach ($viewCache['request_attribute_namespaces'] as $ranName => $ranValues) { $request->setAttributes($ranValues, $ranName); } $nextOutput = $response->getContent(); } else { if ($viewCache['next'] !== null) { // response content was returned from view execute() $response->setContent($nextOutput = $viewCache['next']); $viewCache['next'] = null; } $layers = $viewInstance->getLayers(); if ($isCacheable) { $viewCache['template_variables'] = array(); foreach ($otConfig['template_variables'] as $varName) { $viewCache['template_variables'][$varName] = $viewInstance->getAttribute($varName); } $viewCache['response'] = clone $response; $viewCache['layers'] = array(); $viewCache['slots'] = array(); $lastCacheableLayer = -1; if (is_array($otConfig['layers'])) { if (count($otConfig['layers'])) { for ($i = count($layers) - 1; $i >= 0; $i--) { $layer = $layers[$i]; $layerName = $layer->getName(); if (isset($otConfig['layers'][$layerName])) { if (is_array($otConfig['layers'][$layerName])) { $lastCacheableLayer = $i - 1; } else { $lastCacheableLayer = $i; } } } } } else { $lastCacheableLayer = count($layers) - 1; } for ($i = $lastCacheableLayer + 1; $i < count($layers); $i++) { // $lm->log('Adding non-cacheable layer "' . $layers[$i]->getName() . '" to list'); $viewCache['layers'][] = clone $layers[$i]; } } } $attributes =& $viewInstance->getAttributes(); // whether or not we should assign the previous' layer's output to the $slots array $assignInnerToSlots = $this->getParameter('assign_inner_to_slots', false); // $lm->log('Starting rendering...'); for ($i = 0; $i < count($layers); $i++) { $layer = $layers[$i]; $layerName = $layer->getName(); // $lm->log('Running layer "' . $layerName . '"...'); foreach ($layer->getSlots() as $slotName => $slotContainer) { if ($isViewCached && isset($viewCache['slots'][$layerName][$slotName])) { // $lm->log('Loading cached slot "' . $slotName . '"...'); $slotResponse = $viewCache['slots'][$layerName][$slotName]; } else { // $lm->log('Running slot "' . $slotName . '"...'); $slotResponse = $slotContainer->execute(); if ($isCacheable && !$isViewCached && isset($otConfig['layers'][$layerName]) && is_array($otConfig['layers'][$layerName]) && in_array($slotName, $otConfig['layers'][$layerName])) { // $lm->log('Adding response of slot "' . $slotName . '" to cache...'); $viewCache['slots'][$layerName][$slotName] = $slotResponse; } } // set the presentation data as a template attribute AgaviArrayPathDefinition::setValue($slotName, $output, $slotResponse->getContent()); // and merge the other slot's response (this used to be conditional and done only when the content was not null) // $lm->log('Merging in response from slot "' . $slotName . '"...'); $response->merge($slotResponse); } $moreAssigns = array('container' => $container, 'inner' => $nextOutput, 'request_data' => $container->getRequestData(), 'response' => $response, 'validation_manager' => $container->getValidationManager(), 'view' => $viewInstance); // lock the request. can't be done outside the loop for the whole run, see #628 $key = $request->toggleLock(); try { $nextOutput = $layer->getRenderer()->render($layer, $attributes, $output, $moreAssigns); } catch (Exception $e) { // we caught an exception... unlock the request and rethrow! $request->toggleLock($key); throw $e; } // and unlock the request again $request->toggleLock($key); $response->setContent($nextOutput); if ($isCacheable && !$isViewCached && $i === $lastCacheableLayer) { $viewCache['response'] = clone $response; } $output = array(); if ($assignInnerToSlots) { $output[$layer->getName()] = $nextOutput; } } } if ($isCacheable && !$isViewCached) { // we're writing the view cache first. this is just in case we get into a situation with really bad timing on the leap of a second $viewCache['request_attributes'] = array(); foreach ($otConfig['request_attributes'] as $requestAttribute) { $viewCache['request_attributes'][] = $requestAttribute + array('value' => $request->getAttribute($requestAttribute['name'], $requestAttribute['namespace'])); } $viewCache['request_attribute_namespaces'] = array(); foreach ($otConfig['request_attribute_namespaces'] as $requestAttributeNamespace) { $viewCache['request_attribute_namespaces'][$requestAttributeNamespace] = $request->getAttributes($requestAttributeNamespace); } $this->writeCache($viewGroups, $viewCache, $config['lifetime']); // notify callback that the execution has finished and caches have been written $this->finishedCacheCreationCallback(self::CACHE_CALLBACK_VIEW_CACHE_WRITTEN, $viewGroups, $config, $container); // $lm->log('Writing View cache...'); } } // action cache writing must occur here, so actions that return AgaviView::NONE also get their cache written if ($isCacheable && !$isActionCached) { $actionCache['action_attributes'] = array(); foreach ($config['action_attributes'] as $attributeName) { $actionCache['action_attributes'][$attributeName] = $actionAttributes[$attributeName]; } // $lm->log('Writing Action cache...'); $this->writeCache($actionGroups, $actionCache, $config['lifetime']); // notify callback that the execution has finished and caches have been written $this->finishedCacheCreationCallback(self::CACHE_CALLBACK_ACTION_CACHE_WRITTEN, $actionGroups, $config, $container); } // we're done here. bai. break; } }
/** * Starts the validation process. * * @param AgaviRequestDataHolder The data which should be validated. * * @return bool true, if validation succeeded. * * @author Uwe Mesecke <*****@*****.**> * @since 0.11.0 */ public function execute(AgaviRequestDataHolder $parameters) { $success = true; $this->report = new AgaviValidationReport(); $result = AgaviValidator::SUCCESS; $req = $this->context->getRequest(); $executedValidators = 0; foreach ($this->children as $validator) { ++$executedValidators; $validatorResult = $validator->execute($parameters); $result = max($result, $validatorResult); switch ($validatorResult) { case AgaviValidator::SUCCESS: continue 2; case AgaviValidator::INFO: continue 2; case AgaviValidator::SILENT: continue 2; case AgaviValidator::NOTICE: continue 2; case AgaviValidator::ERROR: $success = false; continue 2; case AgaviValidator::CRITICAL: $success = false; break 2; } } $this->report->setResult($result); $ma = $req->getParameter('module_accessor'); $aa = $req->getParameter('action_accessor'); $umap = $req->getParameter('use_module_action_parameters'); $mode = $this->getParameter('mode'); if ($executedValidators == 0 && $mode == self::MODE_STRICT) { // strict mode and no validators executed -> clear the parameters if ($umap) { $maParam = $parameters->getParameter($ma); $aaParam = $parameters->getParameter($aa); } $parameters->clearAll(); if ($umap) { if ($maParam) { $parameters->setParameter($ma, $maParam); } if ($aaParam) { $parameters->setParameter($aa, $aaParam); } } } if ($mode == self::MODE_STRICT || $executedValidators > 0 && $mode == self::MODE_CONDITIONAL) { // first, we explicitly unset failed arguments // the primary purpose of this is to make sure that arrays that failed validation themselves (e.g. due to array length validation, or due to use of operator validators with an argument base) are removed // that's of course only necessary if validation failed $failedArguments = $this->report->getFailedArguments(); foreach ($failedArguments as $argument) { $parameters->remove($argument->getSource(), $argument->getName()); } // next, we remove all arguments from the request data that are not in the list of succeeded arguments // this will also remove any arguments that didn't have validation rules defined $succeededArguments = $this->report->getSucceededArguments(); foreach ($parameters->getSourceNames() as $source) { $sourceItems = $parameters->getAll($source); foreach (AgaviArrayPathDefinition::getFlatKeyNames($sourceItems) as $name) { if (!isset($succeededArguments[$source . '/' . $name]) && (!$umap || ($source != AgaviRequestDataHolder::SOURCE_PARAMETERS || $name != $ma && $name != $aa))) { $parameters->remove($source, $name); } } } } return $success; }
/** * Returns the flattened version of an array. So the returned array * will be one dimensional with the flattened key names as keys * and their values from the original array as values. * * This method calls itself recursively to flatten the array. * * @param array The array which should be flattened. * @param string The prefix for the key names (only for internal use). * * @return array The flattened array. * * @author Dominik del Bondio <*****@*****.**> * @since 1.0.0 */ public static function flatten($array, $prefix = null) { $flatArray = array(); foreach ($array as $key => $value) { if ($prefix === null) { // create the top node when no prefix was given if (strlen($key) == 0) { // when an empty key was used at top level, create a "relative" path, so the empty string doesn't get lost $name = '[' . $key . ']'; } else { $name = $key; } } else { $name = $prefix . '[' . $key . ']'; } if (is_array($value)) { $flatArray += AgaviArrayPathDefinition::flatten($value, $name); } else { $flatArray[$name] = $value; } } return $flatArray; }
/** * Remove a parameter. * * @param string A parameter name. * * @return string A parameter value, if the parameter was removed, * otherwise null. * * @author Sean Kerr <*****@*****.**> * @since 0.9.0 */ public function &removeParameter($name) { if (isset($this->parameters[$name]) || array_key_exists($name, $this->parameters)) { $retval =& $this->parameters[$name]; unset($this->parameters[$name]); return $retval; } $retval = null; try { $retval =& AgaviArrayPathDefinition::unsetValue($name, $this->parameters); } catch (InvalidArgumentException $e) { } return $retval; }
/** * Sets the value at the path of this instance in the given array. * * @param array The array to set the data in. * @param mixed The value to be set. * * @author Dominik del Bondio <*****@*****.**> * @since 0.11.0 */ public function setValue(array &$array, $value) { AgaviArrayPathDefinition::setValue($this->parts, $array, $value); }
/** * Performs as match of the route against the input * * @param array The route info array. * @param string The input. * @param array The array where the matches will be stored to. * * @return bool Whether the regexp matched. * * @author Dominik del Bondio <*****@*****.**> * @since 0.11.0 */ protected function parseInput(array $route, $input, &$matches) { if ($route['opt']['source'] !== null) { $parts = AgaviArrayPathDefinition::getPartsFromPath($route['opt']['source']); $partArray = $parts['parts']; $count = count($partArray); if ($count > 0 && isset($this->sources[$partArray[0]])) { $input = $this->sources[$partArray[0]]; if ($count > 1) { array_shift($partArray); if (is_array($input)) { $input = AgaviArrayPathDefinition::getValue($partArray, $input); } elseif ($input instanceof AgaviIRoutingSource) { $input = $input->getSource($partArray); } } } } return preg_match($route['rxp'], $input, $matches, PREG_OFFSET_CAPTURE); }
/** * Corrects the order of $_FILES for arrays of files. * The cleaned up array of AgaviUploadedFile objects is returned. * * @param array The array to work on. * @param string Name of the wrapper uploaded file class to instantiate. * @param array Array of indices used during recursion, initially empty. * @param array Output buffer used during recursion, initially empty. * * @author David Zülke <*****@*****.**> * @since 1.1.0 */ protected static function fixFilesArray($input, $uploadedFileClass = 'AgaviUploadedFile', $index = array(), &$output = array()) { $fromIndex = $index; if (count($fromIndex) > 0) { $first = array_shift($fromIndex); array_unshift($fromIndex, $first, 'error'); } $sub = AgaviArrayPathDefinition::getValue($fromIndex, $input); $theIndices = array(); foreach (array('name', 'type', 'size', 'tmp_name', 'error', 'is_uploaded_file') as $name) { $theIndex = $fromIndex; $first = array_shift($theIndex); array_shift($theIndex); array_unshift($theIndex, $first, $name); $theIndices[$name] = $theIndex; } if (is_array($sub)) { foreach ($sub as $key => $value) { $toIndex = array_merge($index, array($key)); if (is_array($value)) { static::fixFilesArray($input, $uploadedFileClass, $toIndex, $output); } else { $data = array(); foreach ($theIndices as $name => $theIndex) { $data[$name] = AgaviArrayPathDefinition::getValue(array_merge($theIndex, array($key)), $input, $name == 'is_uploaded_file' ? true : null); } $data = new $uploadedFileClass($data); AgaviArrayPathDefinition::setValue($toIndex, $output, $data); } } } else { $data = array(); foreach ($theIndices as $name => $theIndex) { $data[$name] = AgaviArrayPathDefinition::getValue($theIndex, $input, $name == 'is_uploaded_file' ? true : null); } $data = new $uploadedFileClass($data); AgaviArrayPathDefinition::setValue($index, $output, $data); } return $output; }