CHANGELOG.md 0000666 00000015315 13527742476 0006407 0 ustar 00 ## 2.3 (2017-11-18) * Deprecated the Silex 1 KnpMenuServiceProvider. Use the `knplabs/knp-menu-silex` package instead. * Fixed RouteVoter to also match on non-string request arguments like integers as long as both string representations are identical. * Add Symfony 4 support ## 2.2 (2016-09-22) * Added a new function to twig: `knp_menu_get_current_item` ## 2.1.1 (2016-01-08) * Made compatible with Symfony 3 ## 2.1.0 (2015-09-20) * Added a new function to twig: `knp_menu_get_breadcrumbs_array` * Added a new filter to twig: `knp_menu_as_string` * Added 2 new tests to twig: `knp_menu_current`, `knp_menu_ancestor` * Made the templates compatible with Twig 2 * Add menu and renderer providers supporting any ArrayAccess implementations. The Pimple-based providers (supporting only Pimple 1) are dperecated in favor of these new providers. ## 2.0.1 (2014-08-01) * Fixed voter conventions on RouteVoter ## 2.0.0 (2014-07-18) * [BC break] Clean code and removed the BC layer ## 2.0.0 beta 1 (2014-06-19) * [BC break] Added the new `Integration` namespace and removed the `Silex` one. * Added a new Voter based on regular expression: `Knp\Menu\Matcher\Voter\RegexVoter` ## 2.0.0 alpha 2 (2014-05-01) * [BC break] Changed the TwigRenderer to accept a menu template only as a string * [BC break] Refactored the way of rendering twig templates. Every template should extends the `knp_menu.html.twig` template. * Introduced extension points in the MenuFactory through `Knp\Menu\Factory\ExtensionInterface` * [BC break compared to 2.0 alpha 1] The inheritance extension points introduced in alpha1 are deprecated in favor of extensions and will be removed before the stable release. * `Knp\Menu\Silex\RouterAwareFactory` is deprecated in favor of `Knp\Menu\Silex\RoutingExtension`. * [BC break] Deprecated the methods `createFromArray` and `createFromNode` in the MenuFactory and removed them from `Knp\Menu\FactoryInterface`. Use `Knp\Menu\Loader\ArrayLoader` and `Knp\Menu\Loader\NodeLoader` instead. * [BC break] Deprecated the methods `moveToPosition`, `moveToFirstPosition`, `moveToLastPosition`, `moveChildToPosition`, `callRecursively`, `toArray`, `getPathAsString` and `getBreadcrumbsArray` in the MenuItem and removed them from `Knp\Menu\ItemInterface`. Use `Knp\Menu\Util\MenuManipulator` instead. * Made the RouterVoter compatible with SensioFrameworkExtraBundle param converters * Added the possibility to match routes using a regex on their name in the RouterVoter * [BC break compared to 2.0 alpha 1] Refactored the RouterVoter to make it more flexible The way to pass routes in the item extras has changed. Before: ```php 'extras' => array( 'routes' => array('foo', 'bar'), 'routeParameters' => array('foo' => array('id' => 4)), ) ``` After: ```php 'extras' => array( 'routes' => array( array('route' => 'foo', 'parameters' => array('id' => 4)), 'bar', ) ) ``` The old syntax is kept until the final release, but using it will trigger a E_USER_DEPRECATED error. ## 2.0.0 alpha 1 (2013-06-23) * Added protected methods `buildOptions` and `configureItem` in the MenuFactory as extension point by inheritance * [BC break] Refactored the way to mark items as current ``setCurrentUri``, ``getCurrentUri`` and ``getCurrentItem`` have been removed from the ItemInterface. Determining the current items is now delegated to a matcher, and the default implementation uses voters to apply the matching. Getting the current items can be done thanks to the CurrentItemFilterIterator. * [BC break] The signature of the CurrentItemFilterIterator constructor changed to accept the item matcher * [BC break] Changed the format of the breadcrumb array Instead of storing the elements with the label as key and the uri as value the array now stores an array of array elements with 3 keys: `label`, `uri` and `item`. ## 1.1.2 (2012-06-10) * Updated the Silex service provider for the change in the interface ## 1.1.1 (2012-05-17) * Added the children attributes and the extras in the array export ## 1.1.0 (2012-05-17) * Marked `Knp\Menu\ItemInterface::getCurrentItem` as deprecated * Added a recursive filter iterator keeping only displayed items * Added a filter iterator keeping only current items * Added a recursive iterator for the item * Fixed building an array of breadcrumbs when a label has only digits * Added a way to mark a label as safe * Refactored the ListRenderer to be consistent with the TwigRenderer and provide the same extension points * Added a way to attach extra data to an item * Removed unnecessary optimization in the TwigRenderer * Added some whitespace control in the Twig template to ensure an empty rendering is really empty * [BC break] Use the childrenAttributes for the root instead of the attributes * Made the default options configurable for the TwigRenderer * Added the support for menu registered as factory in PimpleProvider * Added a way to use the options in `knp_menu_get()` in Twig templates * Added an array of options for the MenuProviderInterface * Added a template to render an ordered list * Refactored the template a bit to make it easier to use an ordered list * Allow omitting the name of the child in `fromArray` (the key is used instead) ## 1.0.0 (2011-12-03) * Add composer.json file * Added more flexible list element blocks * Add support for attributes on the children collection. * Added a default renderer * Added a ChainProvider for the menus. * Added the Silex extension * Added a RouterAwareFactory * Added an helper to be able to reuse the logic more easily for other templating engines * Added a way to retrieve an item using a path in a menu tree * Changed the toArray method to use a depth instead of simply using a boolean flag * Refactored the export to array and the creation from an array * Added better support for encoding problems when escaping a string in the ListRenderer * Added a Twig renderer * Added missing escaping in the ListRenderer * Renamed some methods in the ItemInterface * Removed the configuration of the current item as link from the item * Refactored the ListRenderer to use options * Changed the interface of callRecursively * Refactored the NodeInterface to be consistent * Moved the creation of the item to the factory * Added a Twig extension to render the menu easily * Changed the menu provider interface with a pimple-based implementation * Added a renderer provider to get a renderer by name and a Pimple-based implementation * Removed the renderer from the menu * Removed the num in the item by refactoring isLast and isFirst * Changed the RendererInterface to accept an array of options to be more flexible * Added an ItemInterface * Initial import of KnpMenuBundle decoupled classes with a new namespace LICENSE 0000666 00000002071 13527742476 0005576 0 ustar 00 Copyright (c) 2011-present KnpLabs - https://knplabs.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. composer.json 0000666 00000002542 13527742476 0007316 0 ustar 00 { "name": "knplabs/knp-menu", "type": "library", "description": "An object oriented menu library", "keywords": ["menu", "tree"], "homepage": "https://knplabs.com", "license": "MIT", "authors": [ { "name": "KnpLabs", "homepage": "https://knplabs.com" }, { "name": "Christophe Coevoet", "email": "stof@notk.org" }, { "name": "Symfony Community", "homepage": "https://github.com/KnpLabs/KnpMenu/contributors" } ], "require": { "php": ">=5.6.0" }, "conflict": { "twig/twig": "<1.40|>=2,<2.9" }, "require-dev": { "psr/container": "^1.0", "symfony/http-foundation": "~2.4|~3.0|^4.0", "symfony/phpunit-bridge": "~3.3|^4.0", "symfony/routing": "~2.3|~3.0|^4.0", "twig/twig": "^1.40|^2.9" }, "suggest": { "twig/twig": "for the TwigRenderer and the integration with your templates" }, "autoload": { "psr-4": { "Knp\\Menu\\": "src/Knp/Menu" } }, "autoload-dev": { "psr-4": { "Knp\\Menu\\Tests\\": "tests/Knp/Menu/Tests" } }, "extra": { "branch-alias": { "dev-master": "3.0-dev" } } } src/Knp/Menu/Factory/CoreExtension.php 0000666 00000004103 13527742476 0013717 0 ustar 00 null, 'label' => null, 'attributes' => [], 'linkAttributes' => [], 'childrenAttributes' => [], 'labelAttributes' => [], 'extras' => [], 'current' => null, 'display' => true, 'displayChildren' => true, ], $options ); } /** * Configures the newly created item with the passed options * * @param ItemInterface $item * @param array $options */ public function buildItem(ItemInterface $item, array $options) { $item ->setUri($options['uri']) ->setLabel($options['label']) ->setAttributes($options['attributes']) ->setLinkAttributes($options['linkAttributes']) ->setChildrenAttributes($options['childrenAttributes']) ->setLabelAttributes($options['labelAttributes']) ->setCurrent($options['current']) ->setDisplay($options['display']) ->setDisplayChildren($options['displayChildren']) ; $this->buildExtras($item, $options); } /** * Configures the newly created item's extras * Extras are processed one by one in order not to reset values set by other extensions * * @param ItemInterface $item * @param array $options */ private function buildExtras(ItemInterface $item, array $options) { if (!empty($options['extras'])) { foreach ($options['extras'] as $key => $value) { $item->setExtra($key, $value); } } } } src/Knp/Menu/Factory/ExtensionInterface.php 0000666 00000001053 13527742476 0014730 0 ustar 00 share(function () use ($app) { $factory = new MenuFactory(); if (isset($app['url_generator'])) { $factory->addExtension(new RoutingExtension($app['url_generator'])); } return $factory; }); $app['knp_menu.matcher'] = $app->share(function () use ($app) { $matcher = new Matcher(); if (isset($app['knp_menu.matcher.configure'])) { $app['knp_menu.matcher.configure']($matcher); } return $matcher; }); $app['knp_menu.renderer.list'] = $app->share(function () use ($app) { return new ListRenderer($app['knp_menu.matcher'], [], $app['charset']); }); $app['knp_menu.menu_provider'] = $app->share(function () use ($app) { return new PimpleMenuProvider($app, $app['knp_menu.menus']); }); if (!isset($app['knp_menu.menus'])) { $app['knp_menu.menus'] = []; } $app['knp_menu.renderer_provider'] = $app->share(function () use ($app) { $app['knp_menu.renderers'] = array_merge( ['list' => 'knp_menu.renderer.list'], isset($app['knp_menu.renderer.twig']) ? ['twig' => 'knp_menu.renderer.twig'] : [], isset($app['knp_menu.renderers']) ? $app['knp_menu.renderers'] : [] ); return new PimpleRendererProvider($app, $app['knp_menu.default_renderer'], $app['knp_menu.renderers']); }); $app['knp_menu.menu_manipulator'] = $app->share(function () use ($app) { return new MenuManipulator(); }); if (!isset($app['knp_menu.default_renderer'])) { $app['knp_menu.default_renderer'] = 'list'; } $app['knp_menu.helper'] = $app->share(function () use ($app) { return new Helper($app['knp_menu.renderer_provider'], $app['knp_menu.menu_provider'], $app['knp_menu.menu_manipulator'], $app['knp_menu.matcher']); }); if (isset($app['twig'])) { $app['knp_menu.twig_extension'] = $app->share(function () use ($app) { return new MenuExtension($app['knp_menu.helper'], $app['knp_menu.matcher'], $app['knp_menu.menu_manipulator']); }); $app['knp_menu.renderer.twig'] = $app->share(function () use ($app) { return new TwigRenderer($app['twig'], $app['knp_menu.template'], $app['knp_menu.matcher']); }); if (!isset($app['knp_menu.template'])) { $app['knp_menu.template'] = 'knp_menu.html.twig'; } $app['twig'] = $app->share($app->extend('twig', function (Environment $twig) use ($app) { $twig->addExtension($app['knp_menu.twig_extension']); return $twig; })); $app['twig.loader.filesystem'] = $app->share($app->extend('twig.loader.filesystem', function (FilesystemLoader $loader) use ($app) { $loader->addPath(__DIR__.'/../../Resources/views'); return $loader; })); } } public function boot(Application $app) {} } src/Knp/Menu/Integration/Symfony/RoutingExtension.php 0000666 00000002411 13527742476 0016776 0 ustar 00 generator = $generator; } public function buildOptions(array $options = []) { if (!empty($options['route'])) { $params = isset($options['routeParameters']) ? $options['routeParameters'] : []; $absolute = (isset($options['routeAbsolute']) && $options['routeAbsolute']) ? UrlGeneratorInterface::ABSOLUTE_URL : UrlGeneratorInterface::ABSOLUTE_PATH; $options['uri'] = $this->generator->generate($options['route'], $params, $absolute); // adding the item route to the extras under the 'routes' key (for the Silex RouteVoter) $options['extras']['routes'][] = [ 'route' => $options['route'], 'parameters' => $params, ]; } return $options; } public function buildItem(ItemInterface $item, array $options) { } } src/Knp/Menu/ItemInterface.php 0000666 00000024373 13527742476 0012255 0 ustar 00 tag and is what you should interact with * most of the time by default. * Originally taken from ioMenuPlugin (http://github.com/weaverryan/ioMenuPlugin) */ interface ItemInterface extends \ArrayAccess, \Countable, \IteratorAggregate { /** * @param FactoryInterface $factory * * @return ItemInterface */ public function setFactory(FactoryInterface $factory); /** * @return string */ public function getName(); /** * Renames the item. * * This method must also update the key in the parent. * * Provides a fluent interface * * @param string $name * * @return ItemInterface * * @throws \InvalidArgumentException if the name is already used by a sibling */ public function setName($name); /** * Get the uri for a menu item * * @return string */ public function getUri(); /** * Set the uri for a menu item * * Provides a fluent interface * * @param string $uri The uri to set on this menu item * * @return ItemInterface */ public function setUri($uri); /** * Returns the label that will be used to render this menu item * * Defaults to the name of no label was specified * * @return string */ public function getLabel(); /** * Provides a fluent interface * * @param string $label The text to use when rendering this menu item * * @return ItemInterface */ public function setLabel($label); /** * @return array */ public function getAttributes(); /** * Provides a fluent interface * * @param array $attributes * * @return ItemInterface */ public function setAttributes(array $attributes); /** * @param string $name The name of the attribute to return * @param mixed $default The value to return if the attribute doesn't exist * * @return mixed */ public function getAttribute($name, $default = null); /** * Provides a fluent interface * * @param string $name * @param mixed $value * * @return ItemInterface */ public function setAttribute($name, $value); /** * @return array */ public function getLinkAttributes(); /** * Provides a fluent interface * * @param array $linkAttributes * * @return ItemInterface */ public function setLinkAttributes(array $linkAttributes); /** * @param string $name The name of the attribute to return * @param mixed $default The value to return if the attribute doesn't exist * * @return mixed */ public function getLinkAttribute($name, $default = null); /** * Provides a fluent interface * * @param string $name * @param string $value * * @return ItemInterface */ public function setLinkAttribute($name, $value); /** * @return array */ public function getChildrenAttributes(); /** * Provides a fluent interface * * @param array $childrenAttributes * * @return ItemInterface */ public function setChildrenAttributes(array $childrenAttributes); /** * @param string $name The name of the attribute to return * @param mixed $default The value to return if the attribute doesn't exist * * @return mixed */ public function getChildrenAttribute($name, $default = null); /** * Provides a fluent interface * * @param string $name * @param string $value * * @return ItemInterface */ public function setChildrenAttribute($name, $value); /** * @return array */ public function getLabelAttributes(); /** * Provides a fluent interface * * @param array $labelAttributes * * @return ItemInterface */ public function setLabelAttributes(array $labelAttributes); /** * @param string $name The name of the attribute to return * @param mixed $default The value to return if the attribute doesn't exist * * @return mixed */ public function getLabelAttribute($name, $default = null); /** * Provides a fluent interface * * @param string $name * @param mixed $value * * @return ItemInterface */ public function setLabelAttribute($name, $value); /** * @return array */ public function getExtras(); /** * Provides a fluent interface * * @param array $extras * * @return ItemInterface */ public function setExtras(array $extras); /** * @param string $name The name of the extra to return * @param mixed $default The value to return if the extra doesn't exist * * @return mixed */ public function getExtra($name, $default = null); /** * Provides a fluent interface * * @param string $name * @param mixed $value * * @return ItemInterface */ public function setExtra($name, $value); /** * Whether or not this menu item should show its children. * * @return boolean */ public function getDisplayChildren(); /** * Set whether or not this menu item should show its children * * Provides a fluent interface * * @param boolean $bool * * @return ItemInterface */ public function setDisplayChildren($bool); /** * Whether or not to display this menu item * * @return boolean */ public function isDisplayed(); /** * Set whether or not this menu should be displayed * * Provides a fluent interface * * @param boolean $bool * * @return ItemInterface */ public function setDisplay($bool); /** * Add a child menu item to this menu * * Returns the child item * * @param ItemInterface|string $child An ItemInterface instance or the name of a new item to create * @param array $options If creating a new item, the options passed to the factory for the item * * @return ItemInterface * @throws \InvalidArgumentException if the item is already in a tree */ public function addChild($child, array $options = []); /** * Returns the child menu identified by the given name * * @param string $name Then name of the child menu to return * * @return ItemInterface|null */ public function getChild($name); /** * Reorder children. * * Provides a fluent interface * * @param array $order New order of children. * * @return ItemInterface */ public function reorderChildren($order); /** * Makes a deep copy of menu tree. Every item is copied as another object. * * @return ItemInterface */ public function copy(); /** * Returns the level of this menu item * * The root menu item is 0, followed by 1, 2, etc * * @return integer */ public function getLevel(); /** * Returns the root ItemInterface of this menu tree * * @return ItemInterface */ public function getRoot(); /** * Returns whether or not this menu item is the root menu item * * @return boolean */ public function isRoot(); /** * @return ItemInterface|null */ public function getParent(); /** * Used internally when adding and removing children * * Provides a fluent interface * * @param ItemInterface|null $parent * * @return ItemInterface */ public function setParent(ItemInterface $parent = null); /** * Return the children as an array of ItemInterface objects * * @return ItemInterface[] */ public function getChildren(); /** * Provides a fluent interface * * @param array $children An array of ItemInterface objects * * @return ItemInterface */ public function setChildren(array $children); /** * Removes a child from this menu item * * Provides a fluent interface * * @param ItemInterface|string $name The name of ItemInterface instance or the ItemInterface to remove * * @return ItemInterface */ public function removeChild($name); /** * @return ItemInterface */ public function getFirstChild(); /** * @return ItemInterface */ public function getLastChild(); /** * Returns whether or not this menu items has viewable children * * This menu MAY have children, but this will return false if the current * user does not have access to view any of those items * * @return boolean */ public function hasChildren(); /** * Sets whether or not this menu item is "current". * * If the state is unknown, use null. * * Provides a fluent interface * * @param boolean|null $bool Specify that this menu item is current * * @return ItemInterface */ public function setCurrent($bool); /** * Gets whether or not this menu item is "current". * * @return boolean|null */ public function isCurrent(); /** * Whether this menu item is last in its parent * * @return boolean */ public function isLast(); /** * Whether this menu item is first in its parent * * @return boolean */ public function isFirst(); /** * Whereas isFirst() returns if this is the first child of the parent * menu item, this function takes into consideration whether children are rendered or not. * * This returns true if this is the first child that would be rendered * for the current user * * @return boolean */ public function actsLikeFirst(); /** * Whereas isLast() returns if this is the last child of the parent * menu item, this function takes into consideration whether children are rendered or not. * * This returns true if this is the last child that would be rendered * for the current user * * @return boolean */ public function actsLikeLast(); } src/Knp/Menu/Iterator/CurrentItemFilterIterator.php 0000666 00000000744 13527742476 0016444 0 ustar 00 matcher = $matcher; parent::__construct($iterator); } public function accept() { return $this->matcher->isCurrent($this->current()); } } src/Knp/Menu/Iterator/DisplayedItemFilterIterator.php 0000666 00000000572 13527742476 0016737 0 ustar 00 current()->isDisplayed(); } public function hasChildren() { return $this->current()->getDisplayChildren() && parent::hasChildren(); } } src/Knp/Menu/Iterator/RecursiveItemIterator.php 0000666 00000000546 13527742476 0015623 0 ustar 00 current()); } public function getChildren() { return new static($this->current()); } } src/Knp/Menu/Loader/ArrayLoader.php 0000666 00000002707 13527742476 0013146 0 ustar 00 factory = $factory; } public function load($data) { if (!$this->supports($data)) { throw new \InvalidArgumentException(sprintf('Unsupported data. Expected an array but got ', is_object($data) ? get_class($data) : gettype($data))); } return $this->fromArray($data); } public function supports($data) { return is_array($data); } /** * @param array $data * @param string|null $name (the name of the item, used only if there is no name in the data themselves) * * @return ItemInterface */ private function fromArray(array $data, $name = null) { $name = isset($data['name']) ? $data['name'] : $name; if (isset($data['children'])) { $children = $data['children']; unset($data['children']); } else { $children = []; } $item = $this->factory->createItem($name, $data); foreach ($children as $name => $child) { $item->addChild($this->fromArray($child, $name)); } return $item; } } src/Knp/Menu/Loader/LoaderInterface.php 0000666 00000000645 13527742476 0013767 0 ustar 00 factory = $factory; } public function load($data) { if (!$data instanceof NodeInterface) { throw new \InvalidArgumentException(sprintf('Unsupported data. Expected Knp\Menu\NodeInterface but got %s', is_object($data) ? get_class($data) : gettype($data))); } $item = $this->factory->createItem($data->getName(), $data->getOptions()); foreach ($data->getChildren() as $childNode) { $item->addChild($this->load($childNode)); } return $item; } public function supports($data) { return $data instanceof NodeInterface; } } src/Knp/Menu/Matcher/Matcher.php 0000666 00000004351 13527742476 0012476 0 ustar 00 voters = $voters; $this->cache = new \SplObjectStorage(); } /** * Adds a voter in the matcher. * * If an iterator was used to provide voters in the constructor, it will be * converted to array when using this method, breaking any potential lazy-loading. * * @deprecated since 2.3. Pass voters in the constructor instead. * * @param VoterInterface $voter */ public function addVoter(VoterInterface $voter) { @trigger_error(sprintf('The %s() method is deprecated since version 2.3 and will be removed in 3.0. Pass voters in the constructor instead.', __METHOD__), E_USER_DEPRECATED); if ($this->voters instanceof \Traversable) { $this->voters = iterator_to_array($this->voters); } $this->voters[] = $voter; } public function isCurrent(ItemInterface $item) { $current = $item->isCurrent(); if (null !== $current) { return $current; } if ($this->cache->contains($item)) { return $this->cache[$item]; } foreach ($this->voters as $voter) { $current = $voter->matchItem($item); if (null !== $current) { break; } } $current = (boolean) $current; $this->cache[$item] = $current; return $current; } public function isAncestor(ItemInterface $item, $depth = null) { if (0 === $depth) { return false; } $childDepth = null === $depth ? null : $depth - 1; foreach ($item->getChildren() as $child) { if ($this->isCurrent($child) || $this->isAncestor($child, $childDepth)) { return true; } } return false; } public function clear() { $this->cache = new \SplObjectStorage(); } } src/Knp/Menu/Matcher/MatcherInterface.php 0000666 00000001303 13527742476 0014311 0 ustar 00 regexp = $regexp; } public function matchItem(ItemInterface $item) { if (null === $this->regexp || null === $item->getUri()) { return null; } if (preg_match($this->regexp, $item->getUri())) { return true; } return null; } } src/Knp/Menu/Matcher/Voter/RouteVoter.php 0000666 00000007765 13527742476 0014344 0 ustar 00 requestStack = $requestStack; } elseif ($requestStack instanceof Request) { @trigger_error(sprintf('Passing a Request as the first argument for "%s" constructor is deprecated since version 2.3 and won\'t be possible in 3.0. Pass a RequestStack instead.', __CLASS__), E_USER_DEPRECATED); // BC layer for the old API of the class $this->request = $requestStack; } elseif (null !== $requestStack) { throw new \InvalidArgumentException('The first argument of %s must be null, a RequestStack or a Request. %s given', __CLASS__, is_object($requestStack) ? get_class($requestStack) : gettype($requestStack)); } else { @trigger_error(sprintf('Not passing a RequestStack as the first argument for "%s" constructor is deprecated since version 2.3 and won\'t be possible in 3.0.', __CLASS__), E_USER_DEPRECATED); } } /** * Sets the request against which the menu should be matched. * * This Request is ignored in case a RequestStack is passed in the constructor. * * @deprecated since version 2.3. Pass a RequestStack to the constructor instead. * * @param Request $request */ public function setRequest(Request $request) { @trigger_error(sprintf('The %s() method is deprecated since version 2.3 and will be removed in 3.0. Pass a RequestStack in the constructor instead.', __METHOD__), E_USER_DEPRECATED); $this->request = $request; } public function matchItem(ItemInterface $item) { if (null !== $this->requestStack) { $request = $this->requestStack->getMasterRequest(); } else { $request = $this->request; } if (null === $request) { return null; } $route = $request->attributes->get('_route'); if (null === $route) { return null; } $routes = (array) $item->getExtra('routes', []); foreach ($routes as $testedRoute) { if (is_string($testedRoute)) { $testedRoute = ['route' => $testedRoute]; } if (!is_array($testedRoute)) { throw new \InvalidArgumentException('Routes extra items must be strings or arrays.'); } if ($this->isMatchingRoute($request, $testedRoute)) { return true; } } return null; } private function isMatchingRoute(Request $request, array $testedRoute) { $route = $request->attributes->get('_route'); if (isset($testedRoute['route'])) { if ($route !== $testedRoute['route']) { return false; } } elseif (!empty($testedRoute['pattern'])) { if (!preg_match($testedRoute['pattern'], $route)) { return false; } } else { throw new \InvalidArgumentException('Routes extra items must have a "route" or "pattern" key.'); } if (!isset($testedRoute['parameters'])) { return true; } $routeParameters = $request->attributes->get('_route_params', []); foreach ($testedRoute['parameters'] as $name => $value) { // cast both to string so that we handle integer and other non-string parameters, but don't stumble on 0 == 'abc'. if (!isset($routeParameters[$name]) || (string) $routeParameters[$name] !== (string) $value) { return false; } } return true; } } src/Knp/Menu/Matcher/Voter/UriVoter.php 0000666 00000001007 13527742476 0013764 0 ustar 00 uri = $uri; } public function matchItem(ItemInterface $item) { if (null === $this->uri || null === $item->getUri()) { return null; } if ($item->getUri() === $this->uri) { return true; } return null; } } src/Knp/Menu/Matcher/Voter/VoterInterface.php 0000666 00000000721 13527742476 0015127 0 ustar 00 addExtension(new CoreExtension(), -10); } public function createItem($name, array $options = []) { foreach ($this->getExtensions() as $extension) { $options = $extension->buildOptions($options); } $item = new MenuItem($name, $this); foreach ($this->getExtensions() as $extension) { $extension->buildItem($item, $options); } return $item; } /** * Adds a factory extension * * @param ExtensionInterface $extension * @param integer $priority */ public function addExtension(ExtensionInterface $extension, $priority = 0) { $this->extensions[$priority][] = $extension; $this->sorted = null; } /** * Sorts the internal list of extensions by priority. * * @return ExtensionInterface[] */ private function getExtensions() { if (null === $this->sorted) { krsort($this->extensions); $this->sorted = !empty($this->extensions) ? call_user_func_array('array_merge', $this->extensions) : []; } return $this->sorted; } } src/Knp/Menu/MenuItem.php 0000666 00000031350 13527742476 0011252 0 ustar 00 name = (string) $name; $this->factory = $factory; } /** * setFactory * * @param FactoryInterface $factory * @return self */ public function setFactory(FactoryInterface $factory) { $this->factory = $factory; return $this; } public function getName() { return $this->name; } public function setName($name) { if ($this->name == $name) { return $this; } $parent = $this->getParent(); if (null !== $parent && isset($parent[$name])) { throw new \InvalidArgumentException('Cannot rename item, name is already used by sibling.'); } $oldName = $this->name; $this->name = $name; if (null !== $parent) { $names = array_keys($parent->getChildren()); $items = array_values($parent->getChildren()); $offset = array_search($oldName, $names); $names[$offset] = $name; $parent->setChildren(array_combine($names, $items)); } return $this; } public function getUri() { return $this->uri; } public function setUri($uri) { $this->uri = $uri; return $this; } public function getLabel() { return ($this->label !== null) ? $this->label : $this->name; } public function setLabel($label) { $this->label = $label; return $this; } public function getAttributes() { return $this->attributes; } public function setAttributes(array $attributes) { $this->attributes = $attributes; return $this; } public function getAttribute($name, $default = null) { if (isset($this->attributes[$name])) { return $this->attributes[$name]; } return $default; } public function setAttribute($name, $value) { $this->attributes[$name] = $value; return $this; } public function getLinkAttributes() { return $this->linkAttributes; } public function setLinkAttributes(array $linkAttributes) { $this->linkAttributes = $linkAttributes; return $this; } public function getLinkAttribute($name, $default = null) { if (isset($this->linkAttributes[$name])) { return $this->linkAttributes[$name]; } return $default; } public function setLinkAttribute($name, $value) { $this->linkAttributes[$name] = $value; return $this; } public function getChildrenAttributes() { return $this->childrenAttributes; } public function setChildrenAttributes(array $childrenAttributes) { $this->childrenAttributes = $childrenAttributes; return $this; } public function getChildrenAttribute($name, $default = null) { if (isset($this->childrenAttributes[$name])) { return $this->childrenAttributes[$name]; } return $default; } public function setChildrenAttribute($name, $value) { $this->childrenAttributes[$name] = $value; return $this; } public function getLabelAttributes() { return $this->labelAttributes; } public function setLabelAttributes(array $labelAttributes) { $this->labelAttributes = $labelAttributes; return $this; } public function getLabelAttribute($name, $default = null) { if (isset($this->labelAttributes[$name])) { return $this->labelAttributes[$name]; } return $default; } public function setLabelAttribute($name, $value) { $this->labelAttributes[$name] = $value; return $this; } public function getExtras() { return $this->extras; } public function setExtras(array $extras) { $this->extras = $extras; return $this; } public function getExtra($name, $default = null) { if (isset($this->extras[$name])) { return $this->extras[$name]; } return $default; } public function setExtra($name, $value) { $this->extras[$name] = $value; return $this; } public function getDisplayChildren() { return $this->displayChildren; } public function setDisplayChildren($bool) { $this->displayChildren = (bool) $bool; return $this; } public function isDisplayed() { return $this->display; } public function setDisplay($bool) { $this->display = (bool) $bool; return $this; } public function addChild($child, array $options = []) { if (!$child instanceof ItemInterface) { $child = $this->factory->createItem($child, $options); } elseif (null !== $child->getParent()) { throw new \InvalidArgumentException('Cannot add menu item as child, it already belongs to another menu (e.g. has a parent).'); } $child->setParent($this); $this->children[$child->getName()] = $child; return $child; } public function getChild($name) { return isset($this->children[$name]) ? $this->children[$name] : null; } public function reorderChildren($order) { if (count($order) != $this->count()) { throw new \InvalidArgumentException('Cannot reorder children, order does not contain all children.'); } $newChildren = []; foreach ($order as $name) { if (!isset($this->children[$name])) { throw new \InvalidArgumentException('Cannot find children named ' . $name); } $child = $this->children[$name]; $newChildren[$name] = $child; } $this->setChildren($newChildren); return $this; } public function copy() { $newMenu = clone $this; $newMenu->setChildren([]); $newMenu->setParent(null); foreach ($this->getChildren() as $child) { $newMenu->addChild($child->copy()); } return $newMenu; } public function getLevel() { return $this->parent ? $this->parent->getLevel() + 1 : 0; } public function getRoot() { $obj = $this; do { $found = $obj; } while ($obj = $obj->getParent()); return $found; } public function isRoot() { return null === $this->parent; } public function getParent() { return $this->parent; } public function setParent(ItemInterface $parent = null) { if ($parent === $this) { throw new \InvalidArgumentException('Item cannot be a child of itself'); } $this->parent = $parent; return $this; } public function getChildren() { return $this->children; } public function setChildren(array $children) { $this->children = $children; return $this; } public function removeChild($name) { $name = $name instanceof ItemInterface ? $name->getName() : $name; if (isset($this->children[$name])) { // unset the child and reset it so it looks independent $this->children[$name]->setParent(null); unset($this->children[$name]); } return $this; } public function getFirstChild() { return reset($this->children); } public function getLastChild() { return end($this->children); } public function hasChildren() { foreach ($this->children as $child) { if ($child->isDisplayed()) { return true; } } return false; } public function setCurrent($bool) { $this->isCurrent = $bool; return $this; } public function isCurrent() { return $this->isCurrent; } public function isLast() { // if this is root, then return false if ($this->isRoot()) { return false; } return $this->getParent()->getLastChild() === $this; } public function isFirst() { // if this is root, then return false if ($this->isRoot()) { return false; } return $this->getParent()->getFirstChild() === $this; } public function actsLikeFirst() { // root items are never "marked" as first if ($this->isRoot()) { return false; } // A menu acts like first only if it is displayed if (!$this->isDisplayed()) { return false; } // if we're first and visible, we're first, period. if ($this->isFirst()) { return true; } $children = $this->getParent()->getChildren(); foreach ($children as $child) { // loop until we find a visible menu. If its this menu, we're first if ($child->isDisplayed()) { return $child->getName() === $this->getName(); } } return false; } public function actsLikeLast() { // root items are never "marked" as last if ($this->isRoot()) { return false; } // A menu acts like last only if it is displayed if (!$this->isDisplayed()) { return false; } // if we're last and visible, we're last, period. if ($this->isLast()) { return true; } $children = array_reverse($this->getParent()->getChildren()); foreach ($children as $child) { // loop until we find a visible menu. If its this menu, we're first if ($child->isDisplayed()) { return $child->getName() === $this->getName(); } } return false; } /** * Implements Countable */ public function count() { return count($this->children); } /** * Implements IteratorAggregate */ public function getIterator() { return new \ArrayIterator($this->children); } /** * Implements ArrayAccess */ public function offsetExists($name) { return isset($this->children[$name]); } /** * Implements ArrayAccess */ public function offsetGet($name) { return $this->getChild($name); } /** * Implements ArrayAccess */ public function offsetSet($name, $value) { return $this->addChild($name)->setLabel($value); } /** * Implements ArrayAccess */ public function offsetUnset($name) { $this->removeChild($name); } } src/Knp/Menu/NodeInterface.php 0000666 00000001117 13527742476 0012233 0 ustar 00 registry = $registry; $this->menuIds = $menuIds; } public function get($name, array $options = []) { if (!isset($this->menuIds[$name])) { throw new \InvalidArgumentException(sprintf('The menu "%s" is not defined.', $name)); } $menu = $this->registry[$this->menuIds[$name]]; if (is_callable($menu)) { $menu = call_user_func($menu, $options, $this->registry); } return $menu; } public function has($name, array $options = []) { return isset($this->menuIds[$name]); } } src/Knp/Menu/Provider/ChainProvider.php 0000666 00000001547 13527742476 0014063 0 ustar 00 providers = $providers; } public function get($name, array $options = []) { foreach ($this->providers as $provider) { if ($provider->has($name, $options)) { return $provider->get($name, $options); } } throw new \InvalidArgumentException(sprintf('The menu "%s" is not defined.', $name)); } public function has($name, array $options = []) { foreach ($this->providers as $provider) { if ($provider->has($name, $options)) { return true; } } return false; } } src/Knp/Menu/Provider/LazyProvider.php 0000666 00000002326 13527742476 0013754 0 ustar 00 builders = $builders; } public function get($name, array $options = []) { if (!isset($this->builders[$name])) { throw new \InvalidArgumentException(sprintf('The menu "%s" is not defined.', $name)); } $builder = $this->builders[$name]; if (is_array($builder) && isset($builder[0]) && $builder[0] instanceof \Closure) { $builder[0] = $builder[0](); } if (!is_callable($builder)) { throw new \LogicException(sprintf('Invalid menu builder for "%s". A callable or a factory for an object callable are expected.', $name)); } return call_user_func($builder, $options); } public function has($name, array $options = []) { return isset($this->builders[$name]); } } src/Knp/Menu/Provider/MenuProviderInterface.php 0000666 00000001076 13527742476 0015563 0 ustar 00 container = $container; } public function get($name, array $options = []) { if (!$this->container->has($name)) { throw new \InvalidArgumentException(sprintf('The menu "%s" is not defined.', $name)); } return $this->container->get($name); } public function has($name, array $options = []) { return $this->container->has($name); } } src/Knp/Menu/Renderer/ArrayAccessProvider.php 0000666 00000002251 13527742476 0015206 0 ustar 00 registry = $registry; $this->rendererIds = $rendererIds; $this->defaultRenderer = $defaultRenderer; } public function get($name = null) { if (null === $name) { $name = $this->defaultRenderer; } if (!isset($this->rendererIds[$name])) { throw new \InvalidArgumentException(sprintf('The renderer "%s" is not defined.', $name)); } return $this->registry[$this->rendererIds[$name]]; } public function has($name) { return isset($this->rendererIds[$name]); } } src/Knp/Menu/Renderer/ListRenderer.php 0000666 00000020234 13527742476 0013676 0 ustar 00 matcher = $matcher; $this->defaultOptions = array_merge([ 'depth' => null, 'matchingDepth' => null, 'currentAsLink' => true, 'currentClass' => 'current', 'ancestorClass' => 'current_ancestor', 'firstClass' => 'first', 'lastClass' => 'last', 'compressed' => false, 'allow_safe_labels' => false, 'clear_matcher' => true, 'leaf_class' => null, 'branch_class' => null, ], $defaultOptions); parent::__construct($charset); } public function render(ItemInterface $item, array $options = []) { $options = array_merge($this->defaultOptions, $options); $html = $this->renderList($item, $item->getChildrenAttributes(), $options); if ($options['clear_matcher']) { $this->matcher->clear(); } return $html; } protected function renderList(ItemInterface $item, array $attributes, array $options) { /** * Return an empty string if any of the following are true: * a) The menu has no children eligible to be displayed * b) The depth is 0 * c) This menu item has been explicitly set to hide its children */ if (!$item->hasChildren() || 0 === $options['depth'] || !$item->getDisplayChildren()) { return ''; } $html = $this->format('