.gitignore 0000666 00000000064 13052403053 0006533 0 ustar 00 /phpunit.xml
/vendor/
/composer.lock
/composer.phar
.travis.yml 0000666 00000001074 13052403053 0006656 0 ustar 00 language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache/files
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: COMPOSER_FLAGS='--prefer-lowest --prefer-stable' SYMFONY_DEPRECATIONS_HELPER=weak
- php: 5.6
env: DEPENDENCIES=dev
before_install:
- composer self-update
- if [ "$DEPENDENCIES" = "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi;
install:
- composer update $COMPOSER_FLAGS
script:
- phpunit
CHANGELOG.md 0000666 00000014544 13052403053 0006364 0 ustar 00 ## 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 00000002064 13052403053 0005552 0 ustar 00 Copyright (c) 2011 KnpLabs - http://www.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.
README.markdown 0000666 00000005217 13052403053 0007251 0 ustar 00 KnpMenu
=======
The KnpMenu library provides object oriented menus for PHP 5.3.
It is used by the [KnpMenuBundle](https://github.com/KnpLabs/KnpMenuBundle) for Symfony2
but can now be used stand-alone.
[![Build Status](https://secure.travis-ci.org/KnpLabs/KnpMenu.svg)](http://travis-ci.org/KnpLabs/KnpMenu)
[![Latest Stable Version](https://poser.pugx.org/knplabs/knp-menu/v/stable.svg)](https://packagist.org/packages/knplabs/knp-menu)
[![Latest Unstable Version](https://poser.pugx.org/knplabs/knp-menu/v/unstable.svg)](https://packagist.org/packages/knplabs/knp-menu)
[![Gitter chat](https://badges.gitter.im/KnpLabs/KnpMenu.svg)](https://gitter.im/KnpLabs/KnpMenu)
## Installation
KnpMenu uses Composer, please checkout the [composer website](http://getcomposer.org) for more information.
The simple following command will install `knp-menu` into your project. It also add a new
entry in your `composer.json` and update the `composer.lock` as well.
```bash
$ composer require knplabs/knp-menu
```
> KnpMenu follows the PSR-0 convention names for its classes, which means you can easily integrate `knp-menu` classes loading in your own autoloader.
## Getting Started
```php
createItem('My menu');
$menu->addChild('Home', array('uri' => '/'));
$menu->addChild('Comments', array('uri' => '#comments'));
$menu->addChild('Symfony2', array('uri' => 'http://symfony-reloaded.org/'));
$menu->addChild('Coming soon');
$renderer = new ListRenderer(new \Knp\Menu\Matcher\Matcher());
echo $renderer->render($menu);
```
The above menu would render the following HTML:
```html
```
This way you can finally avoid writing an ugly template to show the selected item,
the first and last items, submenus, ...
> The bulk of the documentation can be found in the `doc` directory.
## What now?
Follow the tutorial in [`doc/01-Basic-Menus.markdown`][0] and [`doc/02-Twig-Integration.markdown`][1]
to discover how KnpMenu will rock your world!
## Credits
This bundle was originally ported from [ioMenuPlugin](http://github.com/weaverryan/ioMenuPlugin),
a menu plugin for symfony1. It has since been developed by [KnpLabs](http://www.knplabs.com) and
the Symfony community.
[0]: doc/01-Basic-Menus.markdown
[1]: doc/02-Twig-Integration.markdown
composer.json 0000666 00000002703 13052403053 0007267 0 ustar 00 {
"name": "knplabs/knp-menu",
"type": "library",
"description": "An object oriented menu library",
"keywords": ["menu", "tree"],
"homepage": "http://knplabs.com",
"license": "MIT",
"authors": [
{
"name": "KnpLabs",
"homepage": "http://knplabs.com"
},
{
"name": "Christophe Coevoet",
"email": "stof@notk.org"
},
{
"name": "Symfony Community",
"homepage": "https://github.com/KnpLabs/KnpMenu/contributors"
}
],
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"pimple/pimple": "~1.0",
"symfony/phpunit-bridge": "~2.7|~3.0",
"symfony/routing": "~2.3|~3.0",
"silex/silex": "~1.0",
"twig/twig": "~1.16|~2.0"
},
"suggest": {
"pimple/pimple": "for the built-in implementations of the menu provider and renderer provider",
"silex/silex": "for the integration with your silex application",
"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": "2.1-dev"
}
}
}
doc/01-Basic-Menus.markdown 0000666 00000022515 13052403053 0011405 0 ustar 00 Creating Menus: The Basics
==========================
Let's face it, creating menus sucks. Menus - a common aspect of any
site - can range from being simple and mundane to giant monsters that
become a headache to code and maintain.
This bundle solves the issue by giving you a small, yet powerful and flexible
framework for handling your menus. While most of the examples shown here
are simple, the menus can grow arbitrarily large and deep.
Creating a menu
---------------
The menu framework centers around one main interface: `Knp\Menu\ItemInterface`.
Items are created by a factory implementing `Knp\Menu\FactoryInterface`.
It's best to think of each `ItemInterface` object as an `` tag that can
hold children objects (`` tags that are wrapped in a `` tag).
For example:
```php
createItem('My menu');
$menu->addChild('Home', array('uri' => '/'));
$menu->addChild('Comments', array('uri' => '#comments'));
$menu->addChild('Symfony2', array('uri' => 'http://symfony-reloaded.org/'));
$renderer = new ListRenderer(new Matcher());
echo $renderer->render($menu);
```
The above would render the following html code:
```html
```
>**NOTE**
>The menu framework automatically adds `first` and `last` classes to each
>`- ` tag at each level for easy styling. Notice also that a `current`
>class is added to the "current" menu item by uri and `current_ancestor`
>to its ancestors (the classes are configurable) The above example assumes
>the menu is being rendered on the `/comments` page, making the Comments
>menu the "current" item.
When the menu is rendered, it's actually spaced correctly so that it appears
as shown in the source html. This is to allow for easier debugging and can
be turned off by passing the `true` as the second argument to the renderer.
```php
true));
echo $renderer->render($menu);
```
You can also compress (or not compress) on a menu-by-menu basis by using the
`compressed` option:
```php
render($menu, array('compressed' => true));
```
Working with your menu tree
---------------------------
Your menu tree works and acts like a multi-dimensional array. Specifically,
it implements ArrayAccess, Countable and Iterator:
```php
createItem('My menu');
$menu->addChild('Home', array('uri' => '/'));
$menu->addChild('Comments');
// ArrayAccess
$menu['Comments']->setUri('#comments');
$menu['Comments']->addChild('My comments', array('uri' => '/my_comments'));
// Countable
echo count($menu); // returns 2
// Iterator
foreach ($menu as $child) {
echo $child->getLabel();
}
```
As you can see, the name you give your menu item (e.g. overview, comments)
when creating it is the name you'll use when accessing it. By default,
the name is also used when displaying the menu, but that can be overridden
by setting the menu item's label (see below).
Customizing each menu item
--------------------------
There are many ways to customize the output of each menu item. Each property
can be customized in two ways: either by passing it as an option when creating
the item, or by using the setter of an existing item.
### The label
By default, a menu item uses its name when rendering. You can easily
change this without changing the name of your menu item by setting its label:
```php
addChild('Home', array('uri' => '/', 'label' => 'Back to homepage'));
// Changing the label of an existing item
$menu['Home']->setLabel('Back to homepage');
```
### The uri
If an item isn't given a url, then text will be output instead of a link:
```php
addChild('Not a link');
$menu->addChild('Home', '/');
$menu->addChild('Symfony', 'http://www.symfony-reloaded.org');
```
You can also specify the uri after creation via the `setUri()` method:
```php
setUri('/');
```
>**NOTE**
>If you want to remove the uri of an item, set it to `null`.
### Menu attributes
In fact, you can add any attribute to the `
- ` tag of a menu item. This
can be done when creating a menu item or via the `setAttribute()` and `setAttributes()`
methods:
```php
addChild('Home', array('attributes' => array('id' => 'back_to_homepage')));
$menu['Home']->setAttribute('id', 'back_to_homepage');
```
>**NOTE**
>`setAttributes()` will overwrite all existing attributes.
>**NOTE**
>To remove an existing attribute, set it to `null`. It will not be rendered.
You can also add link attributes (displayed on the `` element), label
attributes (displayed on the `` element when it is not a link) or
children attributes (rendered on the `
` containing the list of children):
```php
addChild('KnpLabs.com', array('uri' => 'http://knplabs.com'));
$menu['KnpLabs.com']->setLinkAttribute('class', 'external-link');
$menu->addChild('Not a link');
$menu['Not a link']->setLabelAttribute('class', 'no-link-span');
$menu->setChildrenAttribute('class', 'pull-left');
```
>**NOTE**
>For the root element, only the children attributes are used as only the
>`` element is displayed.
>**NOTE**
>In the 1.0 version of the library, the attributes were rendered on the root
>element instead of rendering the children attributes, which was inconsistent
>and has been changed for 1.1.
### Rendering only part of a menu
If you need to render only part of your menu, the menu framework gives
you unlimited control to do so:
```php
render($menu, array('depth' => 2));
// rendering everything except for the children of the Home branch
$menu['Home']->setDisplayChildren(false);
$renderer->render($menu);
// render everything except for Home AND its children
$menu['Home']->setDisplay(false);
$renderer->render($menu);
```
Using the above controls, you can specify exactly which part of your menu
you need to render at any given time.
### Other rendering options
Most renderers also support several other options, which can be passed as
the second argument to the `render()` method:
* `depth`
* `matchingDepth`: The depth of the scan to determine whether an item
is an ancestor of the current item.
* `currentAsLink` (default: `true`)
* `currentClass` (default: `current`)
* `ancestorClass` (default: `current_ancestor`)
* `firstClass` (default: `first`)
* `lastClass` (default: `last`)
* `compressed` (default: `false`)
* `allow_safe_labels` (default: `false`)
* `clear_matcher` (default `true`): whether to clear the internal cache of the matcher after rendering
* `leaf_class` (default: `null`): class for leaf elements in your html tree
* `branch_class` (default: `null`): class for branch elements in your html tree
>**NOTE**
>When setting the `allow_safe_labels` option to `true`, you can specify that
>a label should not be escaped by the renderer by adding the `safe_label`
>extra in the item. Use it with caution as it can create some XSS holes in
>your application if the label is coming from the user.
The Current Menu Item
---------------------
If the menu item is matched as current, a `current` class will be added to
the `li` around that item, as well as a `current_ancestor` around any of
its parent `li` elements. This state can either be forced on the item by
setting it explicitly or matched using several voters.
```php
createItem('My menu');
// set the current state explicitly
$menu['current_item']->setCurrent(true);
$menu['non_current_item']->setCurrent(false);
// Use the voter
$menu['other_item']->setCurrent(null); // default value for items
$matcher = new Matcher();
$matcher->addVoter(new UriVoter($_SERVER['REQUEST_URI']));
$renderer = new ListRenderer($matcher);
```
The library provides 3 implementations of the VoterInterface:
* `Knp\Menu\Matcher\Voter\UriVoter` matching against the uri of the item
* `Knp\Menu\Matcher\Voter\RouteVoter` matching the `_route` attribute of a
Symfony Request object against the `routes` extra of the item
* `Knp\Menu\Matcher\Voter\RegexVoter` matching against the uri of the item using a regular expression
Here are some examples for instantiation of voters:
```php
setRequest($symfonyRequest);
```
Creating a Menu from a Tree structure
-------------------------------------
You can create a menu easily from a Tree structure (a nested set for example) by
making it implement `Knp\Menu\NodeInterface`. You will then be able
to create the menu easily (assuming `$node` is the root node of your structure):
```php
createFromNode($node);
```
Change the charset
------------------
```php
$renderer = new ListRenderer(new Matcher(), [], 'ISO-8859-1');
``` doc/02-Twig-Integration.markdown 0000666 00000021525 13052403053 0012473 0 ustar 00 Twig Integration
================
KnpMenu provides 2 different (and complementary) integrations with Twig:
* [MenuExtension](#menu-extension): a Twig extension allowing you to easily render menus from within a Twig template
* [TwigRenderer](#twig-renderer): a Twig renderer that (behind the scenes) uses a Twig template to render the menu
Using the MenuExtension
-----------------------
The easiest (but not best) way to render a menu inside a Twig template is
to pass both the renderer and menu into a template:
```php
loadTemplate('menu.twig');
echo $template->display(array(
'renderer' => $renderer,
'menu' => $menu
));
```
To render the menu, your template would look like this:
```jinja
{{ renderer.render(menu) | raw }}
```
This is ok, but there is a better way. By configuring all of your renderers
and menus in a central location, you can use a convenient and shorthand syntax
to render your menus inside a Twig template.
### Loading your renderers from a provider
To make life simpler, a good option is to centralize the setup of all of
your renderers. To do this, you'll need to create a "renderer provider", which
is some object - implementing `Knp\Menu\Renderer\RendererProviderInterface` -
which acts like a container for all of your renderers.
The default implementation of the renderer provider is based on [Pimple](http://pimple-project.org/)
so that your renderers can be lazy-loaded.
```php
name of the renderer in pimple
array('main' => 'list_renderer')
);
$helper = new \Knp\Menu\Twig\Helper($rendererProvider);
$menuExtension = new \Knp\Menu\Twig\MenuExtension($helper);
$twig->addExtension($menuExtension);
```
Now, the renderer is aliased to the name `main`. You can render the menu
with the default renderer simply via:
```jinja
{{ knp_menu_render(menu) }}
```
In this example, `menu` variable is the `MenuItem` object you've passed
into your template.
You can also pass options when rendering the template:
```jinja
{{ knp_menu_render(menu, {'currentAsLink': false, 'compressed': true}) }}
```
You can also use another renderer than the default one by passing its name:
```jinja
{{ knp_menu_render(menu, {}, 'main') }}
```
### Retrieving an item by its path in the tree
The Twig extension allow you to retrieve an item in your menu tree by its
path (the name of the item is used in the path):
```jinja
{# The menu variable contains a Knp\Menu\ItemInterface object #}
{% set item = knp_menu_get(menu, ['Comment', 'My comments']) %}
{# The following could be used but would throw a Fatal Error for some invalid
paths instead of an exception: #}
{% set item = menu['Comment']['My comments'] %}
{# actually render the part of the menu #}
{{ knp_menu_render(item) }}
```
>**NOTE**
>An InvalidArgumentException will be thrown if the path is invalid.
Using the path is also supported when using the `knp_menu_render` function
by using an array:
```jinja
{# The menu variable contains a Knp\Menu\ItemInterface object #}
{{ knp_menu_render([menu, 'Comment', 'My comments']) }}
```
### Loading the menu from a provider
The MenuExtension also supports retrieving the menus from a provider implementing
`Knp\Menu\Provider\MenuProviderInterface` which works the same way as the
`RendererProviderInterface`. The default implementation is also based on
Pimple.
```php
createItem('My menu');
// setup the menu
return $menu;
};
$pimple['menu_sidebar'] = ... //
// $rendererProvider = ...
$menuProvider = new \Knp\Menu\Provider\PimpleProvider(
$pimple,
array('main' => 'menu_main', 'sidebar' => 'menu_sidebar')
);
$helper = new \Knp\Menu\Twig\Helper($rendererProvider, $menuProvider);
$menuExtension = new \Knp\Menu\Twig\MenuExtension($helper);
```
You can now retrieve the menu by its name in the template:
```jinja
{% set menu = knp_menu_get('sidebar') %}
{# The menu variable now contains a Knp\Menu\ItemInterface object #}
```
When a menu provider is set, you can also use the menu name instead of the
menu object in the other functions:
```jinja
{{ knp_menu_render('main', {'depth': 1}) }}
{{ knp_menu_render(['main', 'Comments', 'My comments'], {'depth': 2}) }}
{% set item = knp_menu_get('sidebar', ['First section']) %}
{% set breacrumbs_array = knp_menu_get_breadcrumbs_array('main') %}
```
In some cases, you may want to build the menu differently according to the
place it is used. As of KnpMenu 1.1, the ``knp_menu_get`` function supports
passing an array of options for the menu provider.
To be able to use these options in the Pimple provide, you should register
the menu as a factory closure through ``$pimple->protect()``. It will then
be called with the array of options as first argument and the pimple instance
as second argument:
```php
protect(function(array $options, $c) use ($factory) {
$menu = $factory->createItem('My menu');
// setup the menu
// you can use the options you passed to the provider
// and access the pimple container for this.
return $menu;
});
```
Using the TwigRenderer
----------------------
### Registering the renderer
To use the TwigRenderer, you need to add the path of the template in the loader
when bootstrapping Twig.
```php
render($menu);
```
Behind the scenes, the renderer is using a Twig template to render the menu.
This template can be customized by you.
>**NOTE**
>A second template named `knp_menu_ordered.html.twig` can be used if you
>want to render the menu using an ordered list. This template extends the
>previous one which must be available using the `knp_menu.html.twig` name
>(which is the case when configuring the loader like previously).
### Using a custom template
If you need to customize how the template is rendered - beyond all of the
options given to you by modifying the menu items themselves - you can customize
the Twig template that renders the menu.
You can change the template used to render the menu in two different ways:
1) Globally: Change the second argument passed to the constructor of the renderer.
2) Locally: Pass a `template` option when rendering the menu
```php
render($menu, array('template' => 'my_menu.html.twig'));
```
The template needs to contain 2 blocks: `root` and `compressed_root` which
are used to display the root of the menu. The easiest way to customize the
rendering is to extend the built-in template and to replace the block you
want.
>**NOTE**
>The built-in templates contains some additional blocks to make it easier
>to customize it when using the inheritance.
Twig integration reference
--------------------------
### Functions
* `knp_menu_get('menuName' [, ['Path', 'To', 'Item'], ['options']])`: retrieve an item of the menu
* `knp_menu_render('menuName' [, ['options'], 'rendererName'])`: render a menu
* `knp_menu_get_breadcrumbs_array('menuName' [, 'subItem'])`: get an array that represent the breadcrubs of the current page (according to the menu)
### Filters
* `knp_menu_as_string(menuNode [, $separator])`: generate a string representation of the menu item
### Tests
* `knp_menu_current`: check if the item is the current one
* `knp_menu_ancestor`: check if the item is the ancestor of a current item
doc/03-Silex-Integration.markdown 0000666 00000005003 13052403053 0012637 0 ustar 00 Silex Integration
=================
KnpMenu provides an extension for the [Silex](http://silex-project.org/)
microframework.
RouterAwareFactory
------------------
The `Knp\Menu\Silex\RouterAwareFactory` extends the default factory to add
the support of the url generator of the Symfony2 Routing component. You can
then pass 3 new options to the factory:
* `route`: The route name (the generator will be used if the name is not `null`)
* `routeParameters`: The parameters to generate the url (if omitted, an empty array is used)
* `routeAbsolute`: Whether the generated url should be absolute (default `false`)
>**NOTE**
>When you give both a route and an uri to the factory, the route will be used.
Using the KnpMenuExtension
--------------------------
### Registering the extension
```php
registerNamespace('Knp\Menu', __DIR__.'/vendor/KnpMenu/src');
// registering the extension
$app->register(new \Knp\Menu\Integration\Silex\KnpMenuServiceProvider());
```
>**WARNING**
>The Twig integration is available only when the KnpMenuServiceProvider is registered
>**after** the TwigServiceProvider in your application.
#### Parameters
* **knp_menu.menus** (optional): an array of ``alias => id`` pair for the
[menu provider](02-Twig-Integration.markdown#menu-provider).
* **knp_menu.renderers** (optional): an array of ``alias => id`` pair for
the [renderer provider](02-Twig-Integration.markdown#renderer-provider).
* **knp_menu.default_renderer** (optional): the alias of the default renderer (default to `'list'`)
* **knp_menu.template** (optional): The template used by default by the TwigRenderer.
#### Services
* **knp_menu.factory**: The menu factory (it is a router-aware one if the
UrlGeneratorExtension is registered)
* **knp_menu.renderer.list**: The ListRenderer
* **knp_menu.renderer.twig**: The TwigRenderer (only when the Twig integration is available)
### Adding your menu to the menu provider
Making your menu available through the menu provider is really easy. Simply
create the menu as a service in the application and register it in the parameter:
```php
createItem('root');
$menu->addChild('Home', array('route' => 'homepage'));
// ... add more children
return $menu;
};
$app['knp_menu.menus'] = array('main' => 'my_main_menu');
```
Your menu is now available in the [menu provider](02-Twig-Integration.markdown#menu-provider)
with the name `main`.
doc/04-Iterators.md 0000666 00000011144 13052403053 0010030 0 ustar 00 Iterating over Menus
====================
The simplest way to iterate over your item object is to iterate over its
direct children, thanks to the `IteratorAggregate` implementation. But the
PHP iterators allow much more powerful options to deal with our tree structure.
All the examples in this chapter will use the following tree:
```
A
/ \
/ \
B C
/ | \ / \
D E F G H
| |
I J
```
Iterating recursively
---------------------
The `Knp\Menu\Iterator\RecursiveItemIterator` allows you to iterate recursively
over a tree of items. Using it is really easy: give it an item, and you will
have a recursive iterator on this item.
```php
getName() . " ";
}
```
The output will be:
```
B D I E F C G J H
```
Changing the second argument to `\RecursiveIteratorIterator::CHILD_FIRST`
allows you to visit children before their parent and will produce the following
order:
```
I D E F B J G H C
```
>**NOTE**
>If you iterate over the `RecursiveItemIterator` without wrapping it in the
>`\RecursiveIteratorIterator`, it will simply give you the direct children
>like when using the iterator of the item.
As you can see, the final iterator does not contain the root item (``A``).
The reason is simple: the recursive iteration started only on its children.
Fortunately, the `RecursiveItemIterator` accept any iterator over menu items,
not only items themselves. This allows you to add the root item in the final
iterator by changing the iterator wrapped in the `RecursiveItemIterator`:
```php
getName() . " ";
}
```
The output will now contain the root item:
```
A B D I E F C G J H
```
Filtering only current items
----------------------------
Getting the current items is easy with the `Knp\Menu\Iterator\CurrentItemFilterIterator`.
It is a filter iterator applied on another iterator.
```php
getIterator(), $itemMatcher);
foreach ($iterator as $item) {
echo $item->getName() . " ";
}
```
Assuming that D and F are current whereas E is not, this will output ``D F``.
Getting the current item of the whole tree is simply done by applying the
filter on the previous recursive iterator:
```php
getName() . " ";
}
```
Filtering only displayed items
------------------------------
The `Knp\Menu\Iterator\DisplayedItemFilterIterator` allows you to filter
items to keep only displayed ones. As hiding an item also hides its children,
this filter is a recursive filter iterator and is applied on the recursive
iterator, not on the flattened iterator.
```php
getName() . " ";
}
```
Assuming that E and F are hidden, and that C is displayed but hides its children,
the output will be:
```
A B D I C
``` phpunit.xml.dist 0000666 00000000575 13052403053 0007725 0 ustar 00
./tests/
./src
src/Knp/Menu/Factory/CoreExtension.php 0000666 00000004141 13052403053 0013673 0 ustar 00 null,
'label' => null,
'attributes' => array(),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'labelAttributes' => array(),
'extras' => array(),
'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 13052403053 0014702 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'], array(), $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'] = array();
}
$app['knp_menu.renderer_provider'] = $app->share(function () use ($app) {
$app['knp_menu.renderers'] = array_merge(
array('list' => 'knp_menu.renderer.list'),
isset($app['knp_menu.renderer.twig']) ? array('twig' => 'knp_menu.renderer.twig') : array(),
isset($app['knp_menu.renderers']) ? $app['knp_menu.renderers'] : array()
);
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']);
});
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 (\Twig_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 (\Twig_Loader_Filesystem $loader) use ($app) {
$loader->addPath(__DIR__.'/../../Resources/views');
return $loader;
}));
}
}
public function boot(Application $app) {}
}
src/Knp/Menu/Integration/Symfony/RoutingExtension.php 0000666 00000002431 13052403053 0016752 0 ustar 00 generator = $generator;
}
public function buildOptions(array $options = array())
{
if (!empty($options['route'])) {
$params = isset($options['routeParameters']) ? $options['routeParameters'] : array();
$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'][] = array(
'route' => $options['route'],
'parameters' => $params,
);
}
return $options;
}
public function buildItem(ItemInterface $item, array $options)
{
}
}
src/Knp/Menu/ItemInterface.php 0000666 00000024400 13052403053 0012216 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 = array());
/**
* 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 13052403053 0016416 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 13052403053 0016711 0 ustar 00 current()->isDisplayed();
}
public function hasChildren()
{
return $this->current()->getDisplayChildren() && parent::hasChildren();
}
}
src/Knp/Menu/Iterator/RecursiveItemIterator.php 0000666 00000000546 13052403053 0015575 0 ustar 00 current());
}
public function getChildren()
{
return new static($this->current());
}
}
src/Knp/Menu/Loader/ArrayLoader.php 0000666 00000002714 13052403053 0013116 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 = array();
}
$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 13052403053 0013741 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 ', 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 00000003203 13052403053 0012443 0 ustar 00 cache = new \SplObjectStorage();
}
/**
* Adds a voter in the matcher.
*
* @param VoterInterface $voter
*/
public function addVoter(VoterInterface $voter)
{
$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 13052403053 0014263 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 00000004263 13052403053 0014304 0 ustar 00 request = $request;
}
public function setRequest(Request $request)
{
$this->request = $request;
}
public function matchItem(ItemInterface $item)
{
if (null === $this->request) {
return null;
}
$route = $this->request->attributes->get('_route');
if (null === $route) {
return null;
}
$routes = (array) $item->getExtra('routes', array());
foreach ($routes as $testedRoute) {
if (is_string($testedRoute)) {
$testedRoute = array('route' => $testedRoute);
}
if (!is_array($testedRoute)) {
throw new \InvalidArgumentException('Routes extra items must be strings or arrays.');
}
if ($this->isMatchingRoute($testedRoute)) {
return true;
}
}
return null;
}
private function isMatchingRoute(array $testedRoute)
{
$route = $this->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 = $this->request->attributes->get('_route_params', array());
foreach ($testedRoute['parameters'] as $name => $value) {
if (!isset($routeParameters[$name]) || $routeParameters[$name] !== (string) $value) {
return false;
}
}
return true;
}
}
src/Knp/Menu/Matcher/Voter/UriVoter.php 0000666 00000001007 13052403053 0013736 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 13052403053 0015101 0 ustar 00 addExtension(new CoreExtension(), -10);
}
public function createItem($name, array $options = array())
{
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) : array();
}
return $this->sorted;
}
}
src/Knp/Menu/MenuItem.php 0000666 00000030752 13052403053 0011231 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 = array())
{
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 = array();
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(array());
$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)
{
$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 13052403053 0012205 0 ustar 00 registry = $registry;
$this->menuIds = $menuIds;
}
public function get($name, array $options = array())
{
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 = array())
{
return isset($this->menuIds[$name]);
}
}
src/Knp/Menu/Provider/ChainProvider.php 0000666 00000001541 13052403053 0014027 0 ustar 00 providers = $providers;
}
public function get($name, array $options = array())
{
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 = array())
{
foreach ($this->providers as $provider) {
if ($provider->has($name, $options)) {
return true;
}
}
return false;
}
}
src/Knp/Menu/Provider/MenuProviderInterface.php 0000666 00000001110 13052403053 0015522 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 00000020260 13052403053 0013647 0 ustar 00 matcher = $matcher;
$this->defaultOptions = array_merge(array(
'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 = array())
{
$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('renderHtmlAttributes($attributes).'>', 'ul', $item->getLevel(), $options);
$html .= $this->renderChildren($item, $options);
$html .= $this->format('
', 'ul', $item->getLevel(), $options);
return $html;
}
/**
* Renders all of the children of this menu.
*
* This calls ->renderItem() on each menu item, which instructs each
* menu item to render themselves as an - tag (with nested ul if it
* has children).
* This method updates the depth for the children.
*
* @param ItemInterface $item
* @param array $options The options to render the item.
*
* @return string
*/
protected function renderChildren(ItemInterface $item, array $options)
{
// render children with a depth - 1
if (null !== $options['depth']) {
$options['depth'] = $options['depth'] - 1;
}
if (null !== $options['matchingDepth'] && $options['matchingDepth'] > 0) {
$options['matchingDepth'] = $options['matchingDepth'] - 1;
}
$html = '';
foreach ($item->getChildren() as $child) {
$html .= $this->renderItem($child, $options);
}
return $html;
}
/**
* Called by the parent menu item to render this menu.
*
* This renders the li tag to fit into the parent ul as well as its
* own nested ul tag if this menu item has children
*
* @param ItemInterface $item
* @param array $options The options to render the item
*
* @return string
*/
protected function renderItem(ItemInterface $item, array $options)
{
// if we don't have access or this item is marked to not be shown
if (!$item->isDisplayed()) {
return '';
}
// create an array than can be imploded as a class list
$class = (array) $item->getAttribute('class');
if ($this->matcher->isCurrent($item)) {
$class[] = $options['currentClass'];
} elseif ($this->matcher->isAncestor($item, $options['matchingDepth'])) {
$class[] = $options['ancestorClass'];
}
if ($item->actsLikeFirst()) {
$class[] = $options['firstClass'];
}
if ($item->actsLikeLast()) {
$class[] = $options['lastClass'];
}
if ($item->hasChildren() && $options['depth'] !== 0) {
if (null !== $options['branch_class'] && $item->getDisplayChildren()) {
$class[] = $options['branch_class'];
}
} elseif (null !== $options['leaf_class']) {
$class[] = $options['leaf_class'];
}
// retrieve the attributes and put the final class string back on it
$attributes = $item->getAttributes();
if (!empty($class)) {
$attributes['class'] = implode(' ', $class);
}
// opening li tag
$html = $this->format('
- renderHtmlAttributes($attributes).'>', 'li', $item->getLevel(), $options);
// render the text/link inside the li tag
//$html .= $this->format($item->getUri() ? $item->renderLink() : $item->renderLabel(), 'link', $item->getLevel());
$html .= $this->renderLink($item, $options);
// renders the embedded ul
$childrenClass = (array) $item->getChildrenAttribute('class');
$childrenClass[] = 'menu_level_'.$item->getLevel();
$childrenAttributes = $item->getChildrenAttributes();
$childrenAttributes['class'] = implode(' ', $childrenClass);
$html .= $this->renderList($item, $childrenAttributes, $options);
// closing li tag
$html .= $this->format('
', 'li', $item->getLevel(), $options);
return $html;
}
/**
* Renders the link in a a tag with link attributes or
* the label in a span tag with label attributes
*
* Tests if item has a an uri and if not tests if it's
* the current item and if the text has to be rendered
* as a link or not.
*
* @param ItemInterface $item The item to render the link or label for
* @param array $options The options to render the item
*
* @return string
*/
protected function renderLink(ItemInterface $item, array $options = array())
{
if ($item->getUri() && (!$item->isCurrent() || $options['currentAsLink'])) {
$text = $this->renderLinkElement($item, $options);
} else {
$text = $this->renderSpanElement($item, $options);
}
return $this->format($text, 'link', $item->getLevel(), $options);
}
protected function renderLinkElement(ItemInterface $item, array $options)
{
return sprintf('%s', $this->escape($item->getUri()), $this->renderHtmlAttributes($item->getLinkAttributes()), $this->renderLabel($item, $options));
}
protected function renderSpanElement(ItemInterface $item, array $options)
{
return sprintf('%s', $this->renderHtmlAttributes($item->getLabelAttributes()), $this->renderLabel($item, $options));
}
protected function renderLabel(ItemInterface $item, array $options)
{
if ($options['allow_safe_labels'] && $item->getExtra('safe_label', false)) {
return $item->getLabel();
}
return $this->escape($item->getLabel());
}
/**
* If $this->renderCompressed is on, this will apply the necessary
* spacing and line-breaking so that the particular thing being rendered
* makes up its part in a fully-rendered and spaced menu.
*
* @param string $html The html to render in an (un)formatted way
* @param string $type The type [ul,link,li] of thing being rendered
* @param integer $level
* @param array $options
*
* @return string
*/
protected function format($html, $type, $level, array $options)
{
if ($options['compressed']) {
return $html;
}
switch ($type) {
case 'ul':
case 'link':
$spacing = $level * 4;
break;
case 'li':
$spacing = $level * 4 - 2;
break;
}
return str_repeat(' ', $spacing).$html."\n";
}
}
src/Knp/Menu/Renderer/PimpleProvider.php 0000666 00000001116 13052403053 0014205 0 ustar 00 charset = (string) $charset;
}
}
/**
* Renders a HTML attribute
*
* @param string $name
* @param string $value
*
* @return string
*/
protected function renderHtmlAttribute($name, $value)
{
if (true === $value) {
return sprintf('%s="%s"', $name, $this->escape($name));
}
return sprintf('%s="%s"', $name, $this->escape($value));
}
/**
* Renders HTML attributes
*
* @param array $attributes
*
* @return string
*/
protected function renderHtmlAttributes(array $attributes)
{
return implode('', array_map(array($this, 'htmlAttributesCallback'), array_keys($attributes), array_values($attributes)));
}
/**
* Prepares an attribute key and value for HTML representation.
*
* It removes empty attributes.
*
* @param string $name The attribute name
* @param string $value The attribute value
*
* @return string The HTML representation of the HTML key attribute pair.
*/
private function htmlAttributesCallback($name, $value)
{
if (false === $value || null === $value) {
return '';
}
return ' '.$this->renderHtmlAttribute($name, $value);
}
/**
* Escapes an HTML value
*
* @param string $value
*
* @return string
*/
protected function escape($value)
{
return $this->fixDoubleEscape(htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset));
}
/**
* Fixes double escaped strings.
*
* @param string $escaped string to fix
*
* @return string A single escaped string
*/
protected function fixDoubleEscape($escaped)
{
return preg_replace('/&([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', $escaped);
}
/**
* Get the HTML charset
*
* @return string
*/
public function getCharset()
{
return $this->charset;
}
/**
* Set the HTML charset
*
* @param string $charset
*/
public function setCharset($charset)
{
$this->charset = (string) $charset;
}
}
src/Knp/Menu/Renderer/RendererInterface.php 0000666 00000001550 13052403053 0014635 0 ustar 00 environment = $environment;
$this->matcher = $matcher;
$this->defaultOptions = array_merge(array(
'depth' => null,
'matchingDepth' => null,
'currentAsLink' => true,
'currentClass' => 'current',
'ancestorClass' => 'current_ancestor',
'firstClass' => 'first',
'lastClass' => 'last',
'template' => $template,
'compressed' => false,
'allow_safe_labels' => false,
'clear_matcher' => true,
'leaf_class' => null,
'branch_class' => null,
), $defaultOptions);
}
public function render(ItemInterface $item, array $options = array())
{
$options = array_merge($this->defaultOptions, $options);
$html = $this->environment->render($options['template'], array('item' => $item, 'options' => $options, 'matcher' => $this->matcher));
if ($options['clear_matcher']) {
$this->matcher->clear();
}
return $html;
}
}
src/Knp/Menu/Resources/views/knp_menu.html.twig 0000666 00000007553 13052403053 0015562 0 ustar 00 {% extends 'knp_menu_base.html.twig' %}
{% macro attributes(attributes) %}
{% for name, value in attributes %}
{%- if value is not none and value is not same as(false) -%}
{{- ' %s="%s"'|format(name, value is same as(true) ? name|e : value|e)|raw -}}
{%- endif -%}
{%- endfor -%}
{% endmacro %}
{% block compressed_root %}
{% spaceless %}
{{ block('root') }}
{% endspaceless %}
{% endblock %}
{% block root %}
{% set listAttributes = item.childrenAttributes %}
{{ block('list') -}}
{% endblock %}
{% block list %}
{% if item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
{% import _self as knp_menu %}
{% endif %}
{% endblock %}
{% block children %}
{# save current variables #}
{% set currentOptions = options %}
{% set currentItem = item %}
{# update the depth for children #}
{% if options.depth is not none %}
{% set options = options|merge({'depth': currentOptions.depth - 1}) %}
{% endif %}
{# update the matchingDepth for children #}
{% if options.matchingDepth is not none and options.matchingDepth > 0 %}
{% set options = options|merge({'matchingDepth': currentOptions.matchingDepth - 1}) %}
{% endif %}
{% for item in currentItem.children %}
{{ block('item') }}
{% endfor %}
{# restore current variables #}
{% set item = currentItem %}
{% set options = currentOptions %}
{% endblock %}
{% block item %}
{% if item.displayed %}
{# building the class of the item #}
{%- set classes = item.attribute('class') is not empty ? [item.attribute('class')] : [] %}
{%- if matcher.isCurrent(item) %}
{%- set classes = classes|merge([options.currentClass]) %}
{%- elseif matcher.isAncestor(item, options.matchingDepth) %}
{%- set classes = classes|merge([options.ancestorClass]) %}
{%- endif %}
{%- if item.actsLikeFirst %}
{%- set classes = classes|merge([options.firstClass]) %}
{%- endif %}
{%- if item.actsLikeLast %}
{%- set classes = classes|merge([options.lastClass]) %}
{%- endif %}
{# Mark item as "leaf" (no children) or as "branch" (has children that are displayed) #}
{% if item.hasChildren and options.depth is not same as(0) %}
{% if options.branch_class is not empty and item.displayChildren %}
{%- set classes = classes|merge([options.branch_class]) %}
{% endif %}
{% elseif options.leaf_class is not empty %}
{%- set classes = classes|merge([options.leaf_class]) %}
{%- endif %}
{%- set attributes = item.attributes %}
{%- if classes is not empty %}
{%- set attributes = attributes|merge({'class': classes|join(' ')}) %}
{%- endif %}
{# displaying the item #}
{% import _self as knp_menu %}
-
{%- if item.uri is not empty and (not matcher.isCurrent(item) or options.currentAsLink) %}
{{ block('linkElement') }}
{%- else %}
{{ block('spanElement') }}
{%- endif %}
{# render the list of children#}
{%- set childrenClasses = item.childrenAttribute('class') is not empty ? [item.childrenAttribute('class')] : [] %}
{%- set childrenClasses = childrenClasses|merge(['menu_level_' ~ item.level]) %}
{%- set listAttributes = item.childrenAttributes|merge({'class': childrenClasses|join(' ') }) %}
{{ block('list') }}
{% endif %}
{% endblock %}
{% block linkElement %}{% import _self as knp_menu %}{{ block('label') }}{% endblock %}
{% block spanElement %}{% import _self as knp_menu %}{{ block('label') }}{% endblock %}
{% block label %}{% if options.allow_safe_labels and item.getExtra('safe_label', false) %}{{ item.label|raw }}{% else %}{{ item.label }}{% endif %}{% endblock %}
src/Knp/Menu/Resources/views/knp_menu_base.html.twig 0000666 00000000142 13052403053 0016537 0 ustar 00 {% if options.compressed %}{{ block('compressed_root') }}{% else %}{{ block('root') }}{% endif %}
src/Knp/Menu/Resources/views/knp_menu_ordered.html.twig 0000666 00000000456 13052403053 0017261 0 ustar 00 {% extends 'knp_menu.html.twig' %}
{% block list %}
{% import 'knp_menu.html.twig' as macros %}
{% if item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
{{ block('children') }}
{% endif %}
{% endblock %}
src/Knp/Menu/Twig/Helper.php 0000666 00000011202 13052403053 0011624 0 ustar 00 rendererProvider = $rendererProvider;
$this->menuProvider = $menuProvider;
$this->menuManipulator = $menuManipulator;
}
/**
* Retrieves item in the menu, eventually using the menu provider.
*
* @param ItemInterface|string $menu
* @param array $path
* @param array $options
*
* @return ItemInterface
*
* @throws \BadMethodCallException when there is no menu provider and the menu is given by name
* @throws \LogicException
* @throws \InvalidArgumentException when the path is invalid
*/
public function get($menu, array $path = array(), array $options = array())
{
if (!$menu instanceof ItemInterface) {
if (null === $this->menuProvider) {
throw new \BadMethodCallException('A menu provider must be set to retrieve a menu');
}
$menuName = $menu;
$menu = $this->menuProvider->get($menuName, $options);
if (!$menu instanceof ItemInterface) {
throw new \LogicException(sprintf('The menu "%s" exists, but is not a valid menu item object. Check where you created the menu to be sure it returns an ItemInterface object.', $menuName));
}
}
foreach ($path as $child) {
$menu = $menu->getChild($child);
if (null === $menu) {
throw new \InvalidArgumentException(sprintf('The menu has no child named "%s"', $child));
}
}
return $menu;
}
/**
* Renders a menu with the specified renderer.
*
* If the argument is an array, it will follow the path in the tree to
* get the needed item. The first element of the array is the whole menu.
* If the menu is a string instead of an ItemInterface, the provider
* will be used.
*
* @param ItemInterface|string|array $menu
* @param array $options
* @param string $renderer
*
* @return string
*
* @throws \InvalidArgumentException
*/
public function render($menu, array $options = array(), $renderer = null)
{
$menu = $this->castMenu($menu);
return $this->rendererProvider->get($renderer)->render($menu, $options);
}
/**
* Renders an array ready to be used for breadcrumbs.
*
* Each element in the array will be an array with 3 keys:
* - `label` containing the label of the item
* - `url` containing the url of the item (may be `null`)
* - `item` containing the original item (may be `null` for the extra items)
*
* The subItem can be one of the following forms
* * 'subItem'
* * ItemInterface object
* * array('subItem' => '@homepage')
* * array('subItem1', 'subItem2')
* * array(array('label' => 'subItem1', 'url' => '@homepage'), array('label' => 'subItem2'))
*
* @param mixed $item
* @param mixed $subItem A string or array to append onto the end of the array
*
* @return array
*/
public function getBreadcrumbsArray($menu, $subItem = null)
{
if (null === $this->menuManipulator) {
throw new \BadMethodCallException('The menu manipulator must be set to get the breadcrumbs array');
}
$menu = $this->castMenu($menu);
return $this->menuManipulator->getBreadcrumbsArray($menu, $subItem);
}
/**
* @param ItemInterface|array|string $menu
*
* @return ItemInterface
*/
private function castMenu($menu)
{
if (!$menu instanceof ItemInterface) {
$path = array();
if (is_array($menu)) {
if (empty($menu)) {
throw new \InvalidArgumentException('The array cannot be empty');
}
$path = $menu;
$menu = array_shift($path);
}
return $this->get($menu, $path);
}
return $menu;
}
}
src/Knp/Menu/Twig/MenuExtension.php 0000666 00000007752 13052403053 0013225 0 ustar 00 helper = $helper;
$this->matcher = $matcher;
$this->menuManipulator = $menuManipulator;
}
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('knp_menu_get', array($this, 'get')),
new \Twig_SimpleFunction('knp_menu_render', array($this, 'render'), array('is_safe' => array('html'))),
new \Twig_SimpleFunction('knp_menu_get_breadcrumbs_array', array($this, 'getBreadcrumbsArray')),
);
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('knp_menu_as_string', array($this, 'pathAsString')),
);
}
public function getTests()
{
return array(
new \Twig_SimpleTest('knp_menu_current', array($this, 'isCurrent')),
new \Twig_SimpleTest('knp_menu_ancestor', array($this, 'isAncestor')),
);
}
/**
* Retrieves an item following a path in the tree.
*
* @param ItemInterface|string $menu
* @param array $path
* @param array $options
*
* @return ItemInterface
*/
public function get($menu, array $path = array(), array $options = array())
{
return $this->helper->get($menu, $path, $options);
}
/**
* Renders a menu with the specified renderer.
*
* @param ItemInterface|string|array $menu
* @param array $options
* @param string $renderer
*
* @return string
*/
public function render($menu, array $options = array(), $renderer = null)
{
return $this->helper->render($menu, $options, $renderer);
}
/**
* Returns an array ready to be used for breadcrumbs.
*
* @param ItemInterface|array|string $item
* @param string|array|null $subItem
*
* @return array
*/
public function getBreadcrumbsArray($menu, $subItem = null)
{
return $this->helper->getBreadcrumbsArray($menu, $subItem);
}
/**
* A string representation of this menu item
*
* e.g. Top Level > Second Level > This menu
*
* @param ItemInterface $item
* @param string $separator
*
* @return string
*/
public function pathAsString(ItemInterface $menu, $separator = ' > ')
{
if (null === $this->menuManipulator) {
throw new \BadMethodCallException('The menu manipulator must be set to get the breadcrumbs array');
}
return $this->menuManipulator->getPathAsString($menu, $separator);
}
/**
* Checks whether an item is current.
*
* @param ItemInterface $item
*
* @return boolean
*/
public function isCurrent(ItemInterface $item)
{
if (null === $this->matcher) {
throw new \BadMethodCallException('The matcher must be set to get the breadcrumbs array');
}
return $this->matcher->isCurrent($item);
}
/**
* Checks whether an item is the ancestor of a current item.
*
* @param ItemInterface $item
* @param integer $depth The max depth to look for the item
*
* @return boolean
*/
public function isAncestor(ItemInterface $item, $depth = null)
{
if (null === $this->matcher) {
throw new \BadMethodCallException('The matcher must be set to get the breadcrumbs array');
}
return $this->matcher->isAncestor($item);
}
/**
* @return string
*/
public function getName()
{
return 'knp_menu';
}
}
src/Knp/Menu/Util/MenuManipulator.php 0000666 00000022701 13052403053 0013536 0 ustar 00 moveChildToPosition($item->getParent(), $item, $position);
}
/**
* Moves child to specified position. Rearrange other children accordingly.
*
* @param ItemInterface $item
* @param ItemInterface $child Child to move.
* @param integer $position Position to move child to.
*/
public function moveChildToPosition(ItemInterface $item, ItemInterface $child, $position)
{
$name = $child->getName();
$order = array_keys($item->getChildren());
$oldPosition = array_search($name, $order);
unset($order[$oldPosition]);
$order = array_values($order);
array_splice($order, $position, 0, $name);
$item->reorderChildren($order);
}
/**
* Moves item to first position. Rearrange siblings accordingly.
*
* @param ItemInterface $item
*/
public function moveToFirstPosition(ItemInterface $item)
{
$this->moveToPosition($item, 0);
}
/**
* Moves item to last position. Rearrange siblings accordingly.
*
* @param ItemInterface $item
*/
public function moveToLastPosition(ItemInterface $item)
{
$this->moveToPosition($item, $item->getParent()->count());
}
/**
* Get slice of menu as another menu.
*
* If offset and/or length are numeric, it works like in array_slice function:
*
* If offset is non-negative, slice will start at the offset.
* If offset is negative, slice will start that far from the end.
*
* If length is null, slice will have all elements.
* If length is positive, slice will have that many elements.
* If length is negative, slice will stop that far from the end.
*
* It's possible to mix names/object/numeric, for example:
* slice("child1", 2);
* slice(3, $child5);
* Note: when using a child as limit, it will not be included in the returned menu.
* the slice is done before this menu.
*
* @param ItemInterface $item
* @param mixed $offset Name of child, child object, or numeric offset.
* @param mixed $length Name of child, child object, or numeric length.
*
* @return ItemInterface
*/
public function slice(ItemInterface $item, $offset, $length = null)
{
$names = array_keys($item->getChildren());
if ($offset instanceof ItemInterface) {
$offset = $offset->getName();
}
if (!is_numeric($offset)) {
$offset = array_search($offset, $names);
}
if (null !== $length) {
if ($length instanceof ItemInterface) {
$length = $length->getName();
}
if (!is_numeric($length)) {
$index = array_search($length, $names);
$length = ($index < $offset) ? 0 : $index - $offset;
}
}
$slicedItem = $item->copy();
$children = array_slice($slicedItem->getChildren(), $offset, $length);
$slicedItem->setChildren($children);
return $slicedItem;
}
/**
* Split menu into two distinct menus.
*
* @param ItemInterface $item
* @param mixed $length Name of child, child object, or numeric length.
*
* @return array Array with two menus, with "primary" and "secondary" key
*/
public function split(ItemInterface $item, $length)
{
return array(
'primary' => $this->slice($item, 0, $length),
'secondary' => $this->slice($item, $length),
);
}
/**
* Calls a method recursively on all of the children of this item
*
* @example
* $menu->callRecursively('setShowChildren', array(false));
*
* @param ItemInterface $item
* @param string $method
* @param array $arguments
*/
public function callRecursively(ItemInterface $item, $method, $arguments = array())
{
call_user_func_array(array($item, $method), $arguments);
foreach ($item->getChildren() as $child) {
$this->callRecursively($child, $method, $arguments);
}
}
/**
* A string representation of this menu item
*
* e.g. Top Level > Second Level > This menu
*
* @param ItemInterface $item
* @param string $separator
*
* @return string
*/
public function getPathAsString(ItemInterface $item, $separator = ' > ')
{
$children = array();
$obj = $item;
do {
$children[] = $obj->getLabel();
} while ($obj = $obj->getParent());
return implode($separator, array_reverse($children));
}
/**
* @param ItemInterface $item
* @param integer|null $depth the depth until which children should be exported (null means unlimited)
*
* @return array
*/
public function toArray(ItemInterface $item, $depth = null)
{
$array = array(
'name' => $item->getName(),
'label' => $item->getLabel(),
'uri' => $item->getUri(),
'attributes' => $item->getAttributes(),
'labelAttributes' => $item->getLabelAttributes(),
'linkAttributes' => $item->getLinkAttributes(),
'childrenAttributes' => $item->getChildrenAttributes(),
'extras' => $item->getExtras(),
'display' => $item->isDisplayed(),
'displayChildren' => $item->getDisplayChildren(),
'current' => $item->isCurrent(),
);
// export the children as well, unless explicitly disabled
if (0 !== $depth) {
$childDepth = null === $depth ? null : $depth - 1;
$array['children'] = array();
foreach ($item->getChildren() as $key => $child) {
$array['children'][$key] = $this->toArray($child, $childDepth);
}
}
return $array;
}
/**
* Renders an array ready to be used for breadcrumbs.
*
* Each element in the array will be an array with 3 keys:
* - `label` containing the label of the item
* - `url` containing the url of the item (may be `null`)
* - `item` containing the original item (may be `null` for the extra items)
*
* The subItem can be one of the following forms
* * 'subItem'
* * ItemInterface object
* * array('subItem' => '@homepage')
* * array('subItem1', 'subItem2')
* * array(array('label' => 'subItem1', 'url' => '@homepage'), array('label' => 'subItem2'))
*
* @param ItemInterface $item
* @param mixed $subItem A string or array to append onto the end of the array
*
* @return array
*
* @throws \InvalidArgumentException if an element of the subItem is invalid
*/
public function getBreadcrumbsArray(ItemInterface $item, $subItem = null)
{
$breadcrumbs = $this->buildBreadcrumbsArray($item);
if (null === $subItem) {
return $breadcrumbs;
}
if ($subItem instanceof ItemInterface) {
$breadcrumbs[] = $this->getBreadcrumbsItem($subItem);
return $breadcrumbs;
}
if (!is_array($subItem) && !$subItem instanceof \Traversable) {
$subItem = array($subItem);
}
foreach ($subItem as $key => $value) {
switch (true) {
case $value instanceof ItemInterface:
$value = $this->getBreadcrumbsItem($value);
break;
case is_array($value):
// Assume we already have the appropriate array format for the element
break;
case is_integer($key) && is_string($value):
$value = array(
'label' => (string) $value,
'uri' => null,
'item' => null,
);
break;
case is_scalar($value):
$value = array(
'label' => (string) $key,
'uri' => (string) $value,
'item' => null,
);
break;
case null === $value:
$value = array(
'label' => (string) $key,
'uri' => null,
'item' => null,
);
break;
default:
throw new \InvalidArgumentException(sprintf('Invalid value supplied for the key "%s". It should be an item, an array or a scalar', $key));
}
$breadcrumbs[] = $value;
}
return $breadcrumbs;
}
private function buildBreadcrumbsArray(ItemInterface $item)
{
$breadcrumb = array();
do {
$breadcrumb[] = $this->getBreadcrumbsItem($item);
} while ($item = $item->getParent());
return array_reverse($breadcrumb);
}
private function getBreadcrumbsItem(ItemInterface $item)
{
return array(
'label' => $item->getLabel(),
'uri' => $item->getUri(),
'item' => $item,
);
}
}
tests/Knp/Menu/Tests/Factory/CoreExtensionTest.php 0000666 00000005251 13052403053 0016213 0 ustar 00 getExtension();
$item = $this->createItem( 'test' );
$options = $extension->buildOptions( array() );
$this->assertArrayHasKey( 'uri', $options );
$this->assertArrayHasKey( 'label', $options );
$this->assertArrayHasKey( 'attributes', $options );
$this->assertArrayHasKey( 'linkAttributes', $options );
$this->assertArrayHasKey( 'childrenAttributes', $options );
$this->assertArrayHasKey( 'labelAttributes', $options );
$this->assertArrayHasKey( 'extras', $options );
$this->assertArrayHasKey( 'current', $options );
$this->assertArrayHasKey( 'display', $options );
$this->assertArrayHasKey( 'displayChildren', $options );
}
public function testBuildItemsSetsExtras()
{
$item = $this->createItem( 'test' );
$item->setExtra( 'test1', 'original value' );
$extension = $this->getExtension();
$options = $extension->buildOptions(
array(
'extras' => array(
'test1' => 'options value 1',
'test2' => 'options value 2',
)
)
);
$extension->buildItem( $item, $options );
$extras = $item->getExtras();
$this->assertEquals( 2, count( $extras ) );
$this->assertArrayHasKey( 'test1', $extras );
$this->assertEquals( 'options value 1', $item->getExtra( 'test1' ) );
$this->assertArrayHasKey( 'test2', $extras );
$this->assertEquals( 'options value 2', $item->getExtra( 'test2' ) );
}
public function testBuildItemDoesNotOverrideExistingExtras()
{
$item = $this->createItem( 'test' );
$item->setExtra( 'test1', 'original value' );
$extension = $this->getExtension();
$options = $extension->buildOptions(
array(
'extras' => array(
'test2' => 'options value',
)
)
);
$extension->buildItem( $item, $options );
$this->assertArrayHasKey( 'test1', $item->getExtras() );
$this->assertEquals( 'original value', $item->getExtra( 'test1' ) );
}
private function getExtension()
{
return new CoreExtension();
}
private function createItem( $name )
{
$factory = new MenuFactory();
$item = new MenuItem( $name, $factory );
return $item;
}
}
tests/Knp/Menu/Tests/Integration/Silex/KnpMenuServiceProviderTest.php 0000666 00000010323 13052403053 0021773 0 ustar 00 markTestSkipped('Silex is not available');
}
}
public function testFactoryWithoutRouter()
{
$app = new Application();
$app->register(new KnpMenuServiceProvider());
$this->assertEquals('Knp\Menu\MenuFactory', get_class($app['knp_menu.factory']));
}
public function testTwigRendererNotRegistered()
{
$app = new Application();
$app->register(new KnpMenuServiceProvider());
$this->assertFalse(isset($app['knp_menu.renderer.twig']));
}
public function testTwigRendererRegistered()
{
$app = new Application();
$app->register(new TwigServiceProvider());
$app->register(new KnpMenuServiceProvider());
$this->assertTrue(isset($app['knp_menu.renderer.twig']));
}
public function testRenderNotCurrentWithList()
{
$app = $this->bootstrapApp();
$request = Request::create('/list');
$response = $app->handle($request);
$this->assertEquals('', $response->getContent());
}
public function testRenderCurrentWithTwig()
{
$app = $this->bootstrapApp();
$request = Request::create('/twig');
$response = $app->handle($request);
$this->assertEquals('', $response->getContent());
$app = $this->bootstrapApp();
$request = Request::create('/other-twig');
$response = $app->handle($request);
$this->assertEquals('', $response->getContent());
}
private function bootstrapApp()
{
$app = new Application();
$app['debug'] = true;
$app->register(new TwigServiceProvider(), array(
'twig.templates' => array('main' => '{{ knp_menu_render("my_menu", {"compressed": true}, renderer) }}'),
));
$app->register(new KnpMenuServiceProvider(), array(
'knp_menu.menus' => array('my_menu' => 'test.menu.my'),
));
$app->register(new UrlGeneratorServiceProvider());
$app['test.menu.my'] = function (Application $app) {
/** @var $factory \Knp\Menu\FactoryInterface */
$factory = $app['knp_menu.factory'];
$root = $factory->createItem('root', array('childrenAttributes' => array('class' => 'nav')));
$root->addChild('home', array('route' => 'homepage', 'label' => 'Home'));
$root->addChild('KnpLabs', array('uri' => 'http://knplabs.com', 'extras' => array('routes' => 'other_route')));
return $root;
};
$app['test.voter'] = $app->share(function (Application $app) {
$voter = new RouteVoter();
$voter->setRequest($app['request']);
return $voter;
});
$app['knp_menu.matcher.configure'] = $app->protect(function (Matcher $matcher) use ($app) {
$matcher->addVoter($app['test.voter']);
});
$app->get('/twig', function (Application $app) {
return $app['twig']->render('main', array('renderer' => 'twig'));
})->bind('homepage');
$app->get('/other-twig', function (Application $app) {
return $app['twig']->render('main', array('renderer' => 'twig'));
})->bind('other_route');
$app->get('/list', function (Application $app) {
return $app['twig']->render('main', array('renderer' => 'list'));
})->bind('list');
return $app;
}
}
tests/Knp/Menu/Tests/Integration/Symfony/RoutingExtensionTest.php 0000666 00000006607 13052403053 0021300 0 ustar 00 markTestSkipped('The Symfony Routing component is not available');
}
}
public function testCreateItemWithRoute()
{
$generator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
$generator->expects($this->once())
->method('generate')
->with('homepage', array(), UrlGeneratorInterface::ABSOLUTE_PATH)
->will($this->returnValue('/foobar'))
;
$extension = new RoutingExtension($generator);
$processedOptions = $extension->buildOptions(array('uri' => '/hello', 'route' => 'homepage', 'label' => 'foo'));
$this->assertEquals('/foobar', $processedOptions['uri']);
$this->assertEquals('foo', $processedOptions['label']);
}
public function testCreateItemWithRouteAndParameters()
{
$generator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
$generator->expects($this->once())
->method('generate')
->with('homepage', array('id' => 12), UrlGeneratorInterface::ABSOLUTE_PATH)
->will($this->returnValue('/foobar'))
;
$extension = new RoutingExtension($generator);
$processedOptions = $extension->buildOptions(array('route' => 'homepage', 'routeParameters' => array('id' => 12)));
$this->assertEquals('/foobar', $processedOptions['uri']);
}
public function testCreateItemWithAbsoluteRoute()
{
$generator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
$generator->expects($this->once())
->method('generate')
->with('homepage', array(), UrlGeneratorInterface::ABSOLUTE_URL)
->will($this->returnValue('http://php.net'))
;
$extension = new RoutingExtension($generator);
$processedOptions = $extension->buildOptions(array('route' => 'homepage', 'routeAbsolute' => true));
$this->assertEquals('http://php.net', $processedOptions['uri']);
}
public function testCreateItemAppendsRouteUnderExtras()
{
$generator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
$extension = new RoutingExtension($generator);
$processedOptions = $extension->buildOptions( array('route' => 'homepage'));
$this->assertEquals(array(array('route' => 'homepage', 'parameters' => array())), $processedOptions['extras']['routes']);
$processedOptions = $extension->buildOptions( array('route' => 'homepage', 'routeParameters' => array('bar' => 'baz')));
$this->assertEquals(array(array('route' => 'homepage', 'parameters' => array('bar' => 'baz'))), $processedOptions['extras']['routes']);
$processedOptions = $extension->buildOptions( array('route' => 'homepage', 'extras' => array('routes' => array('other_page'))));
$this->assertContains(array('route' => 'homepage', 'parameters' => array()), $processedOptions['extras']['routes']);
$this->assertContains('other_page', $processedOptions['extras']['routes']);
}
}
tests/Knp/Menu/Tests/Iterator/CurrentItemFilterIteratorTest.php 0000666 00000002533 13052403053 0020731 0 ustar 00 pt1->setCurrent(true);
$this->ch2->setCurrent(true);
$this->gc1->setCurrent(true);
$names = array();
// FilterIterator expects an Iterator implementation explicitly, not an IteratorAggregate.
$iterator = new CurrentItemFilterIterator($this->menu->getIterator(), new Matcher());
foreach ($iterator as $value) {
$names[] = $value->getName();
}
$this->assertEquals(array('Parent 1'), $names);
}
public function testFiltering()
{
$this->pt1->setCurrent(true);
$this->ch2->setCurrent(true);
$this->gc1->setCurrent(true);
$names = array();
$iterator = new CurrentItemFilterIterator(
new \RecursiveIteratorIterator(new RecursiveItemIterator($this->menu), \RecursiveIteratorIterator::SELF_FIRST),
new Matcher()
);
foreach ($iterator as $value) {
$names[] = $value->getName();
}
$this->assertEquals(array('Parent 1', 'Child 2', 'Grandchild 1'), $names);
}
}
tests/Knp/Menu/Tests/Iterator/DisplayedItemFilterIteratorTest.php 0000666 00000001452 13052403053 0021224 0 ustar 00 ch1->setDisplay(false);
$this->ch2->setDisplay(false);
$this->ch4->setDisplayChildren(false);
$names = array();
$iterator = new \RecursiveIteratorIterator(
new DisplayedItemFilterIterator(new RecursiveItemIterator($this->menu)),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $value) {
$names[] = $value->getName();
}
$this->assertEquals(array('Parent 1', 'Child 3', 'Parent 2', 'Child 4'), $names);
}
}
tests/Knp/Menu/Tests/Iterator/IteratorTest.php 0000666 00000004307 13052403053 0015402 0 ustar 00 pt1 as $key => $value) {
$count++;
$this->assertEquals('Child '.$count, $key);
$this->assertEquals('Child '.$count, $value->getLabel());
}
}
public function testRecursiveIterator()
{
// Adding an item which does not provide a RecursiveIterator to be sure it works properly.
$child = $this->getMock('Knp\Menu\ItemInterface');
$child->expects($this->any())
->method('getName')
->will($this->returnValue('Foo'));
$child->expects($this->any())
->method('getIterator')
->will($this->returnValue(new \EmptyIterator()));
$this->menu->addChild($child);
$names = array();
foreach (new \RecursiveIteratorIterator(new RecursiveItemIterator($this->menu), \RecursiveIteratorIterator::SELF_FIRST) as $value) {
$names[] = $value->getName();
}
$this->assertEquals(array('Parent 1', 'Child 1', 'Child 2', 'Child 3', 'Parent 2', 'Child 4', 'Grandchild 1', 'Foo'), $names);
}
public function testRecursiveIteratorLeavesOnly()
{
$names = array();
foreach (new \RecursiveIteratorIterator(new RecursiveItemIterator($this->menu), \RecursiveIteratorIterator::LEAVES_ONLY) as $value) {
$names[] = $value->getName();
}
$this->assertEquals(array('Child 1', 'Child 2', 'Child 3', 'Grandchild 1'), $names);
}
public function testFullTreeIterator()
{
$fullTreeIterator = new \RecursiveIteratorIterator(
new RecursiveItemIterator(new \ArrayIterator(array($this->menu))), // recursive iterator containing the root item
\RecursiveIteratorIterator::SELF_FIRST
);
$names = array();
foreach ($fullTreeIterator as $value) {
$names[] = $value->getName();
}
$this->assertEquals(array('Root li', 'Parent 1', 'Child 1', 'Child 2', 'Child 3', 'Parent 2', 'Child 4', 'Grandchild 1'), $names);
}
}
tests/Knp/Menu/Tests/Loader/ArrayLoaderTest.php 0000666 00000005410 13052403053 0015427 0 ustar 00 'joe',
'uri' => '/foobar',
'display' => false,
);
$loader = new ArrayLoader(new MenuFactory());
$item = $loader->load($array);
$this->assertEquals('joe', $item->getName());
$this->assertEquals('/foobar', $item->getUri());
$this->assertFalse($item->isDisplayed());
$this->assertEmpty($item->getAttributes());
$this->assertEmpty($item->getChildren());
}
public function testLoadWithChildren()
{
$array = array(
'name' => 'joe',
'children' => array(
'jack' => array(
'name' => 'jack',
'label' => 'Jack',
),
array(
'name' => 'john'
)
),
);
$loader = new ArrayLoader(new MenuFactory());
$item = $loader->load($array);
$this->assertEquals('joe', $item->getName());
$this->assertEmpty($item->getAttributes());
$this->assertCount(2, $item);
$this->assertTrue(isset($item['john']));
}
public function testLoadWithChildrenOmittingName()
{
$array = array(
'name' => 'joe',
'children' => array(
'jack' => array(
'label' => 'Jack',
),
'john' => array(
'label' => 'John'
)
),
);
$loader = new ArrayLoader(new MenuFactory());
$item = $loader->load($array);
$this->assertEquals('joe', $item->getName());
$this->assertEmpty($item->getAttributes());
$this->assertCount(2, $item);
$this->assertTrue(isset($item['john']));
$this->assertTrue(isset($item['jack']));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testLoadInvalidData()
{
$loader = new ArrayLoader(new MenuFactory());
$loader->load(new \stdClass());
}
/**
* @dataProvider provideSupportingData
*/
public function testSupports($data, $expected)
{
$loader = new ArrayLoader(new MenuFactory());
$this->assertSame($expected, $loader->supports($data));
}
public function provideSupportingData()
{
return array(
array(array(), true),
array(null, false),
array('foobar', false),
array(new \stdClass(), false),
array(53, false),
array(true, false),
);
}
}
tests/Knp/Menu/Tests/Matcher/MatcherTest.php 0000666 00000005444 13052403053 0014771 0 ustar 00 getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('isCurrent')
->will($this->returnValue($flag));
$matcher = new Matcher();
$this->assertSame($expected, $matcher->isCurrent($item));
}
public function provideItemFlag()
{
return array(
array(true, true),
array(false, false),
array(null, false),
);
}
public function testFlagOverwritesCache()
{
$item = $this->getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('isCurrent')
->will($this->onConsecutiveCalls($this->returnValue(true), $this->returnValue(false)));
$matcher = new Matcher();
$this->assertTrue($matcher->isCurrent($item));
$this->assertFalse($matcher->isCurrent($item));
}
/**
* @param boolean $value
*
* @dataProvider provideBoolean
*/
public function testFlagWinsOverVoter($value)
{
$item = $this->getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('isCurrent')
->will($this->returnValue($value));
$voter = $this->getMock('Knp\Menu\Matcher\Voter\VoterInterface');
$voter->expects($this->never())
->method('matchItem');
$matcher = new Matcher();
$matcher->addVoter($voter);
$this->assertSame($value, $matcher->isCurrent($item));
}
/**
* @param boolean $value
*
* @dataProvider provideBoolean
*/
public function testFirstVoterWins($value)
{
$item = $this->getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('isCurrent')
->will($this->returnValue(null));
$voter1 = $this->getMock('Knp\Menu\Matcher\Voter\VoterInterface');
$voter1->expects($this->once())
->method('matchItem')
->with($this->equalTo($item))
->will($this->returnValue($value));
$voter2 = $this->getMock('Knp\Menu\Matcher\Voter\VoterInterface');
$voter2->expects($this->never())
->method('matchItem');
$matcher = new Matcher();
$matcher->addVoter($voter1);
$matcher->addVoter($voter2);
$this->assertSame($value, $matcher->isCurrent($item));
}
public function provideBoolean()
{
return array(
array(true),
array(false),
);
}
}
tests/Knp/Menu/Tests/Matcher/Voter/RegexVoterTest.php 0000666 00000001706 13052403053 0016574 0 ustar 00 getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('getUri')
->will($this->returnValue($itemUri));
$voter = new RegexVoter($exp);
$this->assertSame($expected, $voter->matchItem($item));
}
public function provideData()
{
return array(
'no regexp' => array(null, 'foo', null),
'no item uri' => array('foo', null, null),
'matching uri' => array('/^foo/', 'foobar', true),
'not matching uri' => array('/^foo/', 'barfoo', null),
);
}
}
tests/Knp/Menu/Tests/Matcher/Voter/RouteVoterTest.php 0000666 00000017453 13052403053 0016626 0 ustar 00 markTestSkipped('The Symfony HttpFoundation component is not available.');
}
}
public function testMatchingWithoutRequest()
{
$item = $this->getMock('Knp\Menu\ItemInterface');
$item->expects($this->never())
->method('getExtra');
$voter = new RouteVoter();
$this->assertNull($voter->matchItem($item));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testInvalidRouteConfig()
{
$item = $this->getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('getExtra')
->with($this->logicalOr($this->equalTo('routes'), $this->equalTo('routesParameters')))
->will($this->returnCallback(function ($parameter) {
switch ($parameter) {
case 'routes':
return array(array('invalid' => 'array'));
case 'routesParameters':
return array();
}
}));
$request = new Request();
$request->attributes->set('_route', 'foo');
$request->attributes->set('_route_params', array());
$voter = new RouteVoter($request);
$voter->matchItem($item);
}
/**
* @param string $route
* @param array $parameters
* @param string|array $itemRoutes
* @param array $itemsRoutesParameters
* @param boolean $expected
*
* @dataProvider provideData
*/
public function testMatching($route, array $parameters, $itemRoutes, array $itemsRoutesParameters, $expected)
{
$item = $this->getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('getExtra')
->with($this->logicalOr($this->equalTo('routes'), $this->equalTo('routesParameters')))
->will($this->returnCallback(function ($parameter) use ($itemRoutes, $itemsRoutesParameters) {
switch ($parameter) {
case 'routes':
return $itemRoutes;
case 'routesParameters':
return $itemsRoutesParameters;
}
}))
;
$request = new Request();
$request->attributes->set('_route', $route);
$request->attributes->set('_route_params', $parameters);
$voter = new RouteVoter($request);
$this->assertSame($expected, $voter->matchItem($item));
}
public function provideData()
{
return array(
'no request route' => array(
null,
array(),
'foo',
array(),
null
),
'integer parameters' => array(
'foo',
array('bar' => 128),
array(array('route' => 'foo', 'parameters' => array('bar' => 128))),
array(),
null
),
'no item route' => array(
'foo',
array(),
null,
array(),
null
),
'same single route' => array(
'foo',
array(),
'foo',
array(),
true
),
'different single route' => array(
'foo',
array(),
'bar',
array(),
null
),
'matching multiple routes' => array(
'foo',
array(),
array('foo', 'baz'),
array(),
true
),
'matching multiple routes 2' => array(
'baz',
array(),
array('foo', 'baz'),
array(),
true
),
'different multiple routes' => array(
'foo',
array(),
array('bar', 'baz'),
array(),
null
),
'same single route with different parameters' => array(
'foo',
array('1' => 'bar'),
array(array('route' => 'foo', 'parameters' => array('1' => 'baz'))),
array(),
null
),
'same single route with same parameters' => array(
'foo',
array('1' => 'bar'),
array(array('route' => 'foo', 'parameters' => array('1' => 'bar'))),
array(),
true
),
'same single route with additional parameters' => array(
'foo',
array('1' => 'bar'),
array(array('route' => 'foo', 'parameters' => array('1' => 'bar', '2' >+ 'baz'))),
array(),
null
),
'same single route with less parameters' => array(
'foo',
array('1' => 'bar', '2' => 'baz'),
array(array('route' => 'foo', 'parameters' => array('1' => 'bar'))),
array(),
true
),
'same single route with different type parameters' => array(
'foo',
array('1' => '2'),
array(array('route' => 'foo', 'parameters' => array('1' => 2))),
array(),
true
),
'same route with multiple route params' => array(
'foo',
array('1' => 'bar'),
array(
array('route' => 'foo', 'parameters' => array('1' => 'baz')),
array('route' => 'foo', 'parameters' => array('1' => 'bar')),
),
array(),
true
),
'same route with and without route params' => array(
'foo',
array('1' => 'bar'),
array(
array('route' => 'foo', 'parameters' => array('1' => 'baz')),
array('route' => 'foo'),
),
array(),
true
),
'same route with multiple different route params' => array(
'foo',
array('1' => 'bar'),
array(
array('route' => 'foo', 'parameters' => array('1' => 'baz')),
array('route' => 'foo', 'parameters' => array('1' => 'foo')),
),
array(),
null
),
'matching pattern without parameters' => array(
'foo',
array('1' => 'bar'),
array(array('pattern' => '/fo/')),
array(),
true
),
'non matching pattern without parameters' => array(
'foo',
array('1' => 'bar'),
array(array('pattern' => '/bar/')),
array(),
null
),
'matching pattern with parameters' => array(
'foo',
array('1' => 'bar'),
array(array('pattern' => '/fo/', 'parameters' => array('1' => 'bar'))),
array(),
true
),
'matching pattern with different parameters' => array(
'foo', array('1' => 'bar'),
array(array('pattern' => '/fo/', 'parameters' => array('1' => 'baz'))),
array(),
null
),
);
}
}
tests/Knp/Menu/Tests/Matcher/Voter/UriVoterTest.php 0000666 00000001652 13052403053 0016261 0 ustar 00 getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('getUri')
->will($this->returnValue($itemUri));
$voter = new UriVoter($uri);
$this->assertSame($expected, $voter->matchItem($item));
}
public function provideData()
{
return array(
'no uri' => array(null, 'foo', null),
'no item uri' => array('foo', null, null),
'same uri' => array('foo', 'foo', true),
'different uri' => array('foo', 'bar', null),
);
}
}
tests/Knp/Menu/Tests/MenuFactoryTest.php 0000666 00000003613 13052403053 0014253 0 ustar 00 getMock('Knp\Menu\Factory\ExtensionInterface');
$extension1->expects($this->once())
->method('buildOptions')
->with(array('foo' => 'bar'))
->will($this->returnValue(array('uri' => 'foobar')));
$extension1->expects($this->once())
->method('buildItem')
->with($this->isInstanceOf('Knp\Menu\ItemInterface'), $this->contains('foobar'));
$factory->addExtension($extension1);
$extension2 = $this->getMock('Knp\Menu\Factory\ExtensionInterface');
$extension2->expects($this->once())
->method('buildOptions')
->with(array('foo' => 'baz'))
->will($this->returnValue(array('foo' => 'bar')));
$extension1->expects($this->once())
->method('buildItem')
->with($this->isInstanceOf('Knp\Menu\ItemInterface'), $this->contains('foobar'));
$factory->addExtension($extension2, 10);
$item = $factory->createItem('test', array('foo' => 'baz'));
$this->assertEquals('foobar', $item->getUri());
}
public function testCreateItem()
{
$factory = new MenuFactory();
$item = $factory->createItem('test', array(
'uri' => 'http://example.com',
'linkAttributes' => array('class' => 'foo'),
'display' => false,
'displayChildren' => false,
));
$this->assertInstanceOf('Knp\Menu\ItemInterface', $item);
$this->assertEquals('test', $item->getName());
$this->assertFalse($item->isDisplayed());
$this->assertFalse($item->getDisplayChildren());
$this->assertEquals('foo', $item->getLinkAttribute('class'));
}
}
tests/Knp/Menu/Tests/MenuItemGetterSetterTest.php 0000666 00000016174 13052403053 0016112 0 ustar 00 createMenu();
$this->assertTrue($menu instanceof MenuItem);
}
public function testCreateMenuWithNameAndUri()
{
$menu = $this->createMenu('test1', 'other_uri');
$this->assertEquals('test1', $menu->getName());
$this->assertEquals('other_uri', $menu->getUri());
}
public function testCreateMenuWithTitle()
{
$title = 'This is a test item title';
$menu = $this->createMenu(null, null, array('title' => $title));
$this->assertEquals($title, $menu->getAttribute('title'));
}
public function testName()
{
$menu = $this->createMenu();
$menu->setName('menu name');
$this->assertEquals('menu name', $menu->getName());
}
public function testLabel()
{
$menu = $this->createMenu();
$menu->setLabel('menu label');
$this->assertEquals('menu label', $menu->getLabel());
}
public function testNameIsUsedAsDefaultLabel()
{
$menu = $this->createMenu('My Label');
$this->assertEquals('My Label', $menu->getLabel());
$menu->setLabel('Other Label');
$this->assertEquals('Other Label', $menu->getLabel());
}
public function testUri()
{
$menu = $this->createMenu();
$menu->setUri('menu_uri');
$this->assertEquals('menu_uri', $menu->getUri());
}
public function testAttributes()
{
$attributes = array('class' => 'test_class', 'title' => 'Test title');
$menu = $this->createMenu();
$menu->setAttributes($attributes);
$this->assertEquals($attributes, $menu->getAttributes());
}
public function testDefaultAttribute()
{
$menu = $this->createMenu(null, null, array('id' => 'test_id'));
$this->assertEquals('test_id', $menu->getAttribute('id'));
$this->assertEquals('default_value', $menu->getAttribute('unknown_attribute', 'default_value'));
}
public function testLinkAttributes()
{
$attributes = array('class' => 'test_class', 'title' => 'Test title');
$menu = $this->createMenu();
$menu->setLinkAttributes($attributes);
$this->assertEquals($attributes, $menu->getLinkAttributes());
}
public function testDefaultLinkAttribute()
{
$menu = $this->createMenu();
$menu->setLinkAttribute('class', 'test_class');
$this->assertEquals('test_class', $menu->getLinkAttribute('class'));
$this->assertNull($menu->getLinkAttribute('title'));
$this->assertEquals('foobar', $menu->getLinkAttribute('title', 'foobar'));
}
public function testChildrenAttributes()
{
$attributes = array('class' => 'test_class', 'title' => 'Test title');
$menu = $this->createMenu();
$menu->setChildrenAttributes($attributes);
$this->assertEquals($attributes, $menu->getChildrenAttributes());
}
public function testDefaultChildrenAttribute()
{
$menu = $this->createMenu();
$menu->setChildrenAttribute('class', 'test_class');
$this->assertEquals('test_class', $menu->getChildrenAttribute('class'));
$this->assertNull($menu->getChildrenAttribute('title'));
$this->assertEquals('foobar', $menu->getChildrenAttribute('title', 'foobar'));
}
public function testLabelAttributes()
{
$attributes = array('class' => 'test_class', 'title' => 'Test title');
$menu = $this->createMenu();
$menu->setLabelAttributes($attributes);
$this->assertEquals($attributes, $menu->getLabelAttributes());
}
public function testDefaultLabelAttribute()
{
$menu = $this->createMenu();
$menu->setLabelAttribute('class', 'test_class');
$this->assertEquals('test_class', $menu->getLabelAttribute('class'));
$this->assertNull($menu->getLabelAttribute('title'));
$this->assertEquals('foobar', $menu->getLabelAttribute('title', 'foobar'));
}
public function testExtras()
{
$extras = array('class' => 'test_class', 'title' => 'Test title');
$menu = $this->createMenu();
$menu->setExtras($extras);
$this->assertEquals($extras, $menu->getExtras());
}
public function testDefaultExtras()
{
$menu = $this->createMenu();
$menu->setExtra('class', 'test_class');
$this->assertEquals('test_class', $menu->getExtra('class'));
$this->assertNull($menu->getExtra('title'));
$this->assertEquals('foobar', $menu->getExtra('title', 'foobar'));
}
public function testDisplay()
{
$menu = $this->createMenu();
$this->assertEquals(true, $menu->isDisplayed());
$menu->setDisplay(false);
$this->assertEquals(false, $menu->isDisplayed());
}
public function testShowChildren()
{
$menu = $this->createMenu();
$this->assertEquals(true, $menu->getDisplayChildren());
$menu->setDisplayChildren(false);
$this->assertEquals(false, $menu->getDisplayChildren());
}
public function testParent()
{
$menu = $this->createMenu();
$child = $this->createMenu('child_menu');
$this->assertNull($child->getParent());
$child->setParent($menu);
$this->assertEquals($menu, $child->getParent());
}
public function testChildren()
{
$menu = $this->createMenu();
$child = $this->createMenu('child_menu');
$menu->setChildren(array($child));
$this->assertEquals(array($child), $menu->getChildren());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetExistingNameThrowsAnException()
{
$menu = $this->createMenu();
$menu->addChild('jack');
$menu->addChild('joe');
$menu->getChild('joe')->setName('jack');
}
public function testSetSameName()
{
$parent = $this->getMock('Knp\Menu\ItemInterface');
$parent->expects($this->never())
->method('offsetExists');
$menu = $this->createMenu('my_name');
$menu->setParent($parent);
$menu->setName('my_name');
$this->assertEquals('my_name', $menu->getName());
}
public function testFactory()
{
$child1 = $this->getMock('Knp\Menu\ItemInterface');
$factory = $this->getMock('Knp\Menu\FactoryInterface');
$factory->expects($this->once())
->method('createItem')
->will($this->returnValue($child1));
$menu = $this->createMenu();
$menu->setFactory($factory);
$menu->addChild('child1');
}
/**
* Create a new MenuItem
*
* @param string $name
* @param string $uri
* @param array $attributes
*
* @return \Knp\Menu\MenuItem
*/
protected function createMenu($name = 'test_menu', $uri = 'homepage', array $attributes = array())
{
$factory = new MenuFactory();
return $factory->createItem($name, array('attributes' => $attributes, 'uri' => $uri));
}
}
tests/Knp/Menu/Tests/MenuItemReorderTest.php 0000666 00000002315 13052403053 0015063 0 ustar 00 addChild('c1');
$menu->addChild('c2');
$menu->addChild('c3');
$menu->addChild('c4');
$menu->reorderChildren(array('c4', 'c3', 'c2', 'c1'));
$arr = array_keys($menu->getChildren());
$this->assertEquals(array('c4', 'c3', 'c2', 'c1'), $arr);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testReorderingWithTooManyItemNames()
{
$factory = new MenuFactory();
$menu = new MenuItem('root', $factory);
$menu->addChild('c1');
$menu->reorderChildren(array('c1', 'c3'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testReorderingWithWrongItemNames()
{
$factory = new MenuFactory();
$menu = new MenuItem('root', $factory);
$menu->addChild('c1');
$menu->addChild('c2');
$menu->reorderChildren(array('c1', 'c3'));
}
}
tests/Knp/Menu/Tests/MenuItemTreeTest.php 0000666 00000021073 13052403053 0014362 0 ustar 00 assertCount(2, $this->menu);
$this->assertCount(3, $this->menu['Parent 1']);
$this->assertCount(1, $this->menu['Parent 2']);
$this->assertCount(1, $this->menu['Parent 2']['Child 4']);
$this->assertEquals('Grandchild 1', $this->menu['Parent 2']['Child 4']['Grandchild 1']->getName());
}
public function testGetLevel()
{
$this->assertEquals(0, $this->menu->getLevel());
$this->assertEquals(1, $this->pt1->getLevel());
$this->assertEquals(1, $this->pt2->getLevel());
$this->assertEquals(2, $this->ch4->getLevel());
$this->assertEquals(3, $this->gc1->getLevel());
}
public function testGetRoot()
{
$this->assertSame($this->menu, $this->menu->getRoot());
$this->assertSame($this->menu, $this->pt1->getRoot());
$this->assertSame($this->menu, $this->gc1->getRoot());
}
public function testIsRoot()
{
$this->assertTrue($this->menu->isRoot());
$this->assertFalse($this->pt1->isRoot());
$this->assertFalse($this->ch3->isRoot());
}
public function testGetParent()
{
$this->assertNull($this->menu->getParent());
$this->assertSame($this->menu, $this->pt1->getParent());
$this->assertSame($this->ch4, $this->gc1->getParent());
}
public function testMoveSampleMenuToNewRoot()
{
$newRoot = new TestMenuItem("newRoot", $this->getMock('Knp\Menu\FactoryInterface'));
$newRoot->addChild($this->menu);
$this->assertEquals(1, $this->menu->getLevel());
$this->assertEquals(2, $this->pt1->getLevel());
$this->assertSame($newRoot, $this->menu->getRoot());
$this->assertSame($newRoot, $this->pt1->getRoot());
$this->assertFalse($this->menu->isRoot());
$this->assertTrue($newRoot->isRoot());
$this->assertSame($newRoot, $this->menu->getParent());
}
public function testIsFirst()
{
$this->assertFalse($this->menu->isFirst(), 'The root item is not considered as first');
$this->assertTrue($this->pt1->isFirst());
$this->assertFalse($this->pt2->isFirst());
$this->assertTrue($this->ch4->isFirst());
}
public function testActsLikeFirst()
{
$this->ch1->setDisplay(false);
$this->assertFalse($this->menu->actsLikeFirst(), 'The root item is not considered as first');
$this->assertFalse($this->ch1->actsLikeFirst(), 'A hidden item does not acts like first');
$this->assertTrue($this->ch2->actsLikeFirst());
$this->assertFalse($this->ch3->actsLikeFirst());
$this->assertTrue($this->ch4->actsLikeFirst());
}
public function testActsLikeFirstWithNoDisplayedItem()
{
$this->pt1->setDisplay(false);
$this->pt2->setDisplay(false);
$this->assertFalse($this->pt1->actsLikeFirst());
$this->assertFalse($this->pt2->actsLikeFirst());
}
public function testIsLast()
{
$this->assertFalse($this->menu->isLast(), 'The root item is not considered as last');
$this->assertFalse($this->pt1->isLast());
$this->assertTrue($this->pt2->isLast());
$this->assertTrue($this->ch4->isLast());
}
public function testActsLikeLast()
{
$this->ch3->setDisplay(false);
$this->assertFalse($this->menu->actsLikeLast(), 'The root item is not considered as last');
$this->assertFalse($this->ch1->actsLikeLast());
$this->assertTrue($this->ch2->actsLikeLast());
$this->assertFalse($this->ch3->actsLikeLast(), 'A hidden item does not acts like last');
$this->assertTrue($this->ch4->actsLikeLast());
}
public function testActsLikeLastWithNoDisplayedItem()
{
$this->pt1->setDisplay(false);
$this->pt2->setDisplay(false);
$this->assertFalse($this->pt1->actsLikeLast());
$this->assertFalse($this->pt2->actsLikeLast());
}
public function testArrayAccess()
{
$this->menu->addChild('Child Menu');
$this->assertEquals('Child Menu', $this->menu['Child Menu']->getName());
$this->assertNull($this->menu['Fake']);
$this->menu['New Child'] = 'New Label';
$this->assertEquals('Knp\Menu\MenuItem', get_class($this->menu['New Child']));
$this->assertEquals('New Child', $this->menu['New Child']->getName());
$this->assertEquals('New Label', $this->menu['New Child']->getLabel());
unset($this->menu['New Child']);
$this->assertNull($this->menu['New Child']);
}
public function testCountable()
{
$this->assertCount(2, $this->menu);
$this->menu->addChild('New Child');
$this->assertCount(3, $this->menu);
unset($this->menu['New Child']);
$this->assertCount(2, $this->menu);
}
public function testGetChildren()
{
$children = $this->ch4->getChildren();
$this->assertCount(1, $children);
$this->assertEquals($this->gc1->getName(), $children['Grandchild 1']->getName());
}
public function testGetFirstChild()
{
$this->assertSame($this->pt1, $this->menu->getFirstChild());
// test for bug in getFirstChild implementation (when internal array pointer is changed getFirstChild returns wrong child)
foreach ($this->menu->getChildren() as $c);
$this->assertSame($this->pt1, $this->menu->getFirstChild());
}
public function testGetLastChild()
{
$this->assertSame($this->pt2, $this->menu->getLastChild());
// test for bug in getFirstChild implementation (when internal array pointer is changed getLastChild returns wrong child)
foreach ($this->menu->getChildren() as $c);
$this->assertSame($this->pt2, $this->menu->getLastChild());
}
public function testAddChildDoesNotUSeTheFactoryIfItem()
{
$factory = $this->getMock('Knp\Menu\FactoryInterface');
$factory->expects($this->never())
->method('createItem');
$menu = new MenuItem('Root li', $factory);
$menu->addChild(new MenuItem('Child 3', $factory));
}
/**
* @expectedException \LogicException
*/
public function testAddChildFailsIfInAnotherMenu()
{
$factory = $this->getMock('Knp\Menu\FactoryInterface');
$menu = new MenuItem('Root li', $factory);
$child = new MenuItem('Child 3', $factory);
$menu->addChild($child);
$menu2 = new MenuItem('Second menu', $factory);
$menu2->addChild($child);
}
public function testGetChild()
{
$this->assertSame($this->gc1, $this->ch4->getChild('Grandchild 1'));
$this->assertNull($this->ch4->getChild('nonexistentchild'));
}
public function testRemoveChild()
{
$gc2 = $this->ch4->addChild('gc2');
$gc3 = $this->ch4->addChild('gc3');
$gc4 = $this->ch4->addChild('gc4');
$this->assertCount(4, $this->ch4);
$this->ch4->removeChild('gc4');
$this->assertCount(3, $this->ch4);
$this->assertTrue($this->ch4->getChild('Grandchild 1')->isFirst());
$this->assertTrue($this->ch4->getChild('gc3')->isLast());
}
public function testRemoveFakeChild()
{
$this->menu->removeChild('fake');
$this->assertCount(2, $this->menu);
}
public function testReAddRemovedChild()
{
$gc2 = $this->ch4->addChild('gc2');
$this->ch4->removeChild('gc2');
$this->menu->addChild($gc2);
$this->assertCount(3, $this->menu);
$this->assertTrue($gc2->isLast());
$this->assertFalse($this->pt2->isLast());
}
public function testUpdateChildAfterRename()
{
$this->pt1->setName('Temp name');
$this->assertSame($this->pt1, $this->menu->getChild('Temp name'));
$this->assertEquals(array('Temp name', 'Parent 2'), array_keys($this->menu->getChildren()));
$this->assertNull($this->menu->getChild('Parent 1'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRenameToExistingSiblingNameThrowAnException()
{
$this->pt1->setName('Parent 2');
}
public function testGetUri()
{
$this->addChildWithExternalUrl();
$this->assertNull($this->pt1->getUri());
$this->assertEquals('http://www.symfony-reloaded.org', $this->menu['child']->getUri());
}
protected function addChildWithExternalUrl()
{
$this->menu->addChild('child', array('uri' => 'http://www.symfony-reloaded.org'));
}
}
tests/Knp/Menu/Tests/Provider/ArrayAccessProviderTest.php 0000666 00000003045 13052403053 0017523 0 ustar 00 'first', 'second' => 'dummy'));
$this->assertTrue($provider->has('first'));
$this->assertTrue($provider->has('second'));
$this->assertFalse($provider->has('third'));
}
public function testGetExistentMenu()
{
$registry = new \ArrayObject();
$menu = $this->getMock('Knp\Menu\ItemInterface');
$registry['menu'] = $menu;
$provider = new ArrayAccessProvider($registry, array('default' => 'menu'));
$this->assertSame($menu, $provider->get('default'));
}
public function testGetMenuAsClosure()
{
$registry = new \ArrayObject();
$menu = $this->getMock('Knp\Menu\ItemInterface');
$registry['menu'] = function ($options, $c) use ($menu) {
$c['options'] = $options;
return $menu;
};
$provider = new ArrayAccessProvider($registry, array('default' => 'menu'));
$this->assertSame($menu, $provider->get('default', array('foo' => 'bar')));
$this->assertEquals(array('foo' => 'bar'), $registry['options']);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testGetNonExistentMenu()
{
$provider = new ArrayAccessProvider(new \ArrayObject());
$provider->get('non-existent');
}
}
tests/Knp/Menu/Tests/Provider/ChainProviderTest.php 0000666 00000005022 13052403053 0016342 0 ustar 00 getMock('Knp\Menu\Provider\MenuProviderInterface');
$innerProvider->expects($this->at(0))
->method('has')
->with('first')
->will($this->returnValue(true))
;
$innerProvider->expects($this->at(1))
->method('has')
->with('second')
->will($this->returnValue(false))
;
$innerProvider->expects($this->at(2))
->method('has')
->with('third', array('foo' => 'bar'))
->will($this->returnValue(false))
;
$provider = new ChainProvider(array($innerProvider));
$this->assertTrue($provider->has('first'));
$this->assertFalse($provider->has('second'));
$this->assertFalse($provider->has('third', array('foo' => 'bar')));
}
public function testGetExistentMenu()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$innerProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$innerProvider->expects($this->any())
->method('has')
->with('default')
->will($this->returnValue(true))
;
$innerProvider->expects($this->once())
->method('get')
->with('default')
->will($this->returnValue($menu))
;
$provider = new ChainProvider(array($innerProvider));
$this->assertSame($menu, $provider->get('default'));
}
public function testGetWithOptions()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$innerProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$innerProvider->expects($this->any())
->method('has')
->with('default', array('foo' => 'bar'))
->will($this->returnValue(true))
;
$innerProvider->expects($this->once())
->method('get')
->with('default', array('foo' => 'bar'))
->will($this->returnValue($menu))
;
$provider = new ChainProvider(array($innerProvider));
$this->assertSame($menu, $provider->get('default', array('foo' => 'bar')));
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetNonExistentMenu()
{
$provider = new ChainProvider(array());
$provider->get('non-existent');
}
}
tests/Knp/Menu/Tests/Provider/PimpleProviderTest.php 0000666 00000003336 13052403053 0016554 0 ustar 00 markTestSkipped('Pimple is not available');
}
}
public function testHas()
{
$provider = new PimpleProvider(new \Pimple(), array('first' => 'first', 'second' => 'dummy'));
$this->assertTrue($provider->has('first'));
$this->assertTrue($provider->has('second'));
$this->assertFalse($provider->has('third'));
}
public function testGetExistentMenu()
{
$pimple = new \Pimple();
$menu = $this->getMock('Knp\Menu\ItemInterface');
$pimple['menu'] = function() use ($menu) {
return $menu;
};
$provider = new PimpleProvider($pimple, array('default' => 'menu'));
$this->assertSame($menu, $provider->get('default'));
}
public function testGetMenuAsClosure()
{
$pimple = new \Pimple();
$menu = $this->getMock('Knp\Menu\ItemInterface');
$pimple['menu'] = $pimple->protect(function($options, $c) use ($menu) {
$c['options'] = $options;
return $menu;
});
$provider = new PimpleProvider($pimple, array('default' => 'menu'));
$this->assertSame($menu, $provider->get('default', array('foo' => 'bar')));
$this->assertEquals(array('foo' => 'bar'), $pimple['options']);
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetNonExistentMenu()
{
$provider = new PimpleProvider(new \Pimple());
$provider->get('non-existent');
}
}
tests/Knp/Menu/Tests/Renderer/AbstractRendererTest.php 0000666 00000036567 13052403053 0017035 0 ustar 00 matcher = new Matcher();
$this->renderer = $this->createRenderer($this->matcher);
}
abstract protected function createRenderer(MatcherInterface $matcher);
protected function tearDown()
{
parent::tearDown();
$this->renderer = null;
}
public function testRenderEmptyRoot()
{
$menu = new MenuItem('test', new MenuFactory());
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderRootWithAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->setChildrenAttributes(array('class' => 'test_class'));
$menu->addChild('c1');
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderEncodedAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->setChildrenAttributes(array('title' => 'encode " me >'));
$menu->addChild('c1');
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderLink()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('uri' => '/about'));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderLinkWithAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('uri' => '/about', 'linkAttributes' => array('title' => 'About page')));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderLinkWithEmptyAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array(
'uri' => '/about',
'linkAttributes' => array('title' => '', 'rel' => null, 'target' => false)
));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderLinkWithSpecialAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('uri' => '/about', 'linkAttributes' => array('title' => true)));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderChildrenWithAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$about = $menu->addChild('About');
$about->addChild('Us');
$about->setChildrenAttribute('title', 'About page');
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderChildrenWithEmptyAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$about = $menu->addChild('About');
$about->addChild('Us');
$about->setChildrenAttribute('title', '');
$about->setChildrenAttribute('rel', null);
$about->setChildrenAttribute('target', false);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderChildrenWithSpecialAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$about = $menu->addChild('About');
$about->addChild('Us');
$about->setChildrenAttribute('title', true);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderLabelWithAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('labelAttributes' => array('title' => 'About page')));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderLabelWithEmptyAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('labelAttributes' => array('title' => '', 'rel' => null, 'target' => false)));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderLabelWithSpecialAttributes()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('labelAttributes' => array('title' => true)));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderWeirdLink()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('uri' => 'http://en.wikipedia.org/wiki/%22Weird_Al%22_Yankovic?v1=1&v2=2'));
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderEscapedLabel()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('label' => 'Encode " me'));
$menu->addChild('Safe', array('label' => 'Encode " me again', 'extras' => array('safe_label' => true)));
$menu->addChild('Escaped', array('label' => 'Encode " me too', 'extras' => array('safe_label' => false)));
$rendered = '- Encode " me
- Encode " me again
- Encode " me too
';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderSafeLabel()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About', array('label' => 'Encode " me'));
$menu->addChild('Safe', array('label' => 'Encode " me again', 'extras' => array('safe_label' => true)));
$menu->addChild('Escaped', array('label' => 'Encode " me too', 'extras' => array('safe_label' => false)));
$rendered = '- Encode " me
- Encode " me again
- Encode " me too
';
$this->assertEquals($rendered, $this->renderer->render($menu, array('allow_safe_labels' => true)));
}
public function testRenderWholeMenu()
{
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu));
}
public function testRenderWithClassAndTitle()
{
$this->pt2->setAttribute('class', 'parent2_class');
$this->pt2->setAttribute('title', 'parent2 title');
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu));
}
public function testRenderWithCurrentItem()
{
$this->ch2->setCurrent(true);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu));
}
public function testRenderWithCurrentItemAsLink()
{
$menu = new MenuItem('test', new MenuFactory());
$about = $menu->addChild('About', array('uri' => '/about'));
$about->setCurrent(true);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu));
}
public function testRenderWithCurrentItemNotAsLink()
{
$menu = new MenuItem('test', new MenuFactory());
$about = $menu->addChild('About', array('uri' => '/about'));
$about->setCurrent(true);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($menu, array('currentAsLink' => false)));
}
public function testRenderSubMenuPortionWithClassAndTitle()
{
$this->pt2->setChildrenAttribute('class', 'parent2_class')->setChildrenAttribute('title', 'parent2 title');
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu['Parent 2']));
}
public function testDoNotShowChildrenRendersNothing()
{
$this->menu->setDisplayChildren(false);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu));
}
public function testDoNotShowChildChildrenRendersPartialMenu()
{
$this->menu['Parent 1']->setDisplayChildren(false);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu));
}
public function testDoNotShowChildRendersPartialMenu()
{
$this->menu['Parent 1']->setDisplay(false);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu));
}
public function testDepth0()
{
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 0)));
}
public function testDepth1()
{
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 1)));
}
public function testDepth2()
{
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 2)));
}
public function testDepth2WithNotShowChildChildren()
{
$this->menu['Parent 1']->setDisplayChildren(false);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 2)));
}
public function testEmptyUncompressed()
{
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 0, 'compressed' => false)));
}
public function testMatchingDepth0()
{
$this->menu['Parent 1']['Child 1']->setCurrent(true);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 1,'matchingDepth' => 1)));
}
public function testMatchingDepth1()
{
$this->menu['Parent 1']['Child 1']->setCurrent(true);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 1,'matchingDepth' => 2)));
}
public function testMatchingDepth2()
{
$this->menu['Parent 1']['Child 1']->setCurrent(true);
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 1,'matchingDepth' => 0)));
}
public function testLeafAndBranchRendering()
{
$rendered = '';
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('depth' => 2, 'leaf_class' => 'leaf', 'branch_class' => 'branch')));
}
}
tests/Knp/Menu/Tests/Renderer/ArrayAccessProviderTest.php 0000666 00000002713 13052403053 0017500 0 ustar 00 'first', 'second' => 'dummy'));
$this->assertTrue($provider->has('first'));
$this->assertTrue($provider->has('second'));
$this->assertFalse($provider->has('third'));
}
public function testGetExistentRenderer()
{
$registry = new \Pimple();
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$registry['renderer'] = $renderer;
$provider = new ArrayAccessProvider($registry, 'default', array('default' => 'renderer'));
$this->assertSame($renderer, $provider->get('default'));
}
public function testGetDefaultRenderer()
{
$registry = new \ArrayObject();
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$registry['renderer'] = $renderer;
$provider = new ArrayAccessProvider($registry, 'default', array('default' => 'renderer'));
$this->assertSame($renderer, $provider->get());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testGetNonExistentRenderer()
{
$provider = new ArrayAccessProvider(new \ArrayObject(), 'default', array());
$provider->get('non-existent');
}
}
tests/Knp/Menu/Tests/Renderer/ListRendererTest.php 0000666 00000001276 13052403053 0016172 0 ustar 00 true));
return $renderer;
}
public function testPrettyRendering()
{
$rendered = <<
-
Parent 1
-
Parent 2
HTML;
$this->assertEquals($rendered, $this->renderer->render($this->menu, array('compressed' => false, 'depth' => 1)));
}
}
tests/Knp/Menu/Tests/Renderer/PimpleProviderTest.php 0000666 00000003272 13052403053 0016527 0 ustar 00 markTestSkipped('Pimple is not available');
}
}
public function testHas()
{
$provider = new PimpleProvider(new \Pimple(), 'first', array('first' => 'first', 'second' => 'dummy'));
$this->assertTrue($provider->has('first'));
$this->assertTrue($provider->has('second'));
$this->assertFalse($provider->has('third'));
}
public function testGetExistentRenderer()
{
$pimple = new \Pimple();
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$pimple['renderer'] = function() use ($renderer) {
return $renderer;
};
$provider = new PimpleProvider($pimple, 'default', array('default' => 'renderer'));
$this->assertSame($renderer, $provider->get('default'));
}
public function testGetDefaultRenderer()
{
$pimple = new \Pimple();
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$pimple['renderer'] = function() use ($renderer) {
return $renderer;
};
$provider = new PimpleProvider($pimple, 'default', array('default' => 'renderer'));
$this->assertSame($renderer, $provider->get());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testGetNonExistentRenderer()
{
$provider = new PimpleProvider(new \Pimple(), 'default', array());
$provider->get('non-existent');
}
}
tests/Knp/Menu/Tests/Renderer/TwigRendererTest.php 0000666 00000002214 13052403053 0016162 0 ustar 00 markTestSkipped('Twig is not available');
}
$loader = new \Twig_Loader_Filesystem(__DIR__.'/../../../../../src/Knp/Menu/Resources/views');
$environment = new \Twig_Environment($loader);
$renderer = new TwigRenderer($environment, 'knp_menu.html.twig', $matcher, array('compressed' => true));
return $renderer;
}
public function testRenderOrderedList()
{
$menu = new MenuItem('test', new MenuFactory());
$menu->addChild('About')->addChild('foobar');
$rendered = '- About
';
$this->assertEquals($rendered, $this->renderer->render($menu, array('template' => 'knp_menu_ordered.html.twig')));
}
}
tests/Knp/Menu/Tests/TestCase.php 0000666 00000004124 13052403053 0012670 0 ustar 00 menu = $factory->createItem('Root li', array('childrenAttributes' => array('class' => 'root')));
$this->pt1 = $this->menu->addChild('Parent 1');
$this->ch1 = $this->pt1->addChild('Child 1');
$this->ch2 = $this->pt1->addChild('Child 2');
// add the 3rd child via addChild with an object
$this->ch3 = new MenuItem('Child 3', $factory);
$this->pt1->addChild($this->ch3);
$this->pt2 = $this->menu->addChild('Parent 2');
$this->ch4 = $this->pt2->addChild('Child 4');
$this->gc1 = $this->ch4->addChild('Grandchild 1');
}
protected function tearDown()
{
$this->menu = null;
$this->pt1 = null;
$this->ch1 = null;
$this->ch2 = null;
$this->ch3 = null;
$this->pt2 = null;
$this->ch4 = null;
$this->gc1 = null;
}
// prints a visual representation of our basic testing tree
protected function printTestTree()
{
print(' Menu Structure '."\n");
print(' rt '."\n");
print(' / \ '."\n");
print(' pt1 pt2 '."\n");
print(' / | \ | '."\n");
print(' ch1 ch2 ch3 ch4 '."\n");
print(' | '."\n");
print(' gc1 '."\n");
}
}
tests/Knp/Menu/Tests/Twig/HelperTest.php 0000666 00000021652 13052403053 0014153 0 ustar 00 getMock('Knp\Menu\ItemInterface');
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$renderer->expects($this->once())
->method('render')
->with($menu, array())
->will($this->returnValue('foobar
'))
;
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$rendererProvider->expects($this->once())
->method('get')
->with(null)
->will($this->returnValue($renderer))
;
$helper = new Helper($rendererProvider);
$this->assertEquals('foobar
', $helper->render($menu));
}
public function testRenderMenuWithOptions()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$renderer->expects($this->once())
->method('render')
->with($menu, array('firstClass' => 'test'))
->will($this->returnValue('foobar
'))
;
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$rendererProvider->expects($this->once())
->method('get')
->with(null)
->will($this->returnValue($renderer))
;
$helper = new Helper($rendererProvider);
$this->assertEquals('foobar
', $helper->render($menu, array('firstClass' => 'test')));
}
public function testRenderMenuWithRenderer()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$renderer->expects($this->once())
->method('render')
->with($menu, array())
->will($this->returnValue('foobar
'))
;
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$rendererProvider->expects($this->once())
->method('get')
->with('custom')
->will($this->returnValue($renderer))
;
$helper = new Helper($rendererProvider);
$this->assertEquals('foobar
', $helper->render($menu, array(), 'custom'));
}
public function testRenderMenuByName()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$menuProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$menuProvider->expects($this->once())
->method('get')
->with('default')
->will($this->returnValue($menu))
;
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$renderer->expects($this->once())
->method('render')
->with($menu, array())
->will($this->returnValue('foobar
'))
;
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$rendererProvider->expects($this->once())
->method('get')
->with(null)
->will($this->returnValue($renderer))
;
$helper = new Helper($rendererProvider, $menuProvider);
$this->assertEquals('foobar
', $helper->render('default'));
}
public function testGetMenu()
{
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$menuProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$menu = $this->getMock('Knp\Menu\ItemInterface');
$menuProvider->expects($this->once())
->method('get')
->with('default')
->will($this->returnValue($menu))
;
$helper = new Helper($rendererProvider, $menuProvider);
$this->assertSame($menu, $helper->get('default'));
}
/**
* @expectedException LogicException
*/
public function testGetMenuWithBadReturnValue()
{
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$menuProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$menuProvider->expects($this->once())
->method('get')
->with('default')
->will($this->returnValue(new \stdClass()))
;
$helper = new Helper($rendererProvider, $menuProvider);
$helper->get('default');
}
/**
* @expectedException BadMethodCallException
*/
public function testGetMenuWithoutProvider()
{
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$helper = new Helper($rendererProvider);
$helper->get('default');
}
public function testGetMenuWithOptions()
{
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$menuProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$menu = $this->getMock('Knp\Menu\ItemInterface');
$menuProvider->expects($this->once())
->method('get')
->with('default', array('foo' => 'bar'))
->will($this->returnValue($menu))
;
$helper = new Helper($rendererProvider, $menuProvider);
$this->assertSame($menu, $helper->get('default', array(), array('foo' => 'bar')));
}
public function testGetMenuByPath()
{
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$menuProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$child = $this->getMock('Knp\Menu\ItemInterface');
$menu = $this->getMock('Knp\Menu\ItemInterface');
$menu->expects($this->any())
->method('getChild')
->with('child')
->will($this->returnValue($child))
;
$menuProvider->expects($this->once())
->method('get')
->with('default')
->will($this->returnValue($menu))
;
$helper = new Helper($rendererProvider, $menuProvider);
$this->assertSame($child, $helper->get('default', array('child')));
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetMenuByInvalidPath()
{
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$menuProvider = $this->getMock('Knp\Menu\Provider\MenuProviderInterface');
$child = $this->getMock('Knp\Menu\ItemInterface');
$child->expects($this->any())
->method('getChild')
->will($this->returnValue(null))
;
$menu = $this->getMock('Knp\Menu\ItemInterface');
$menu->expects($this->any())
->method('getChild')
->with('child')
->will($this->returnValue($child))
;
$menuProvider->expects($this->once())
->method('get')
->with('default')
->will($this->returnValue($menu))
;
$helper = new Helper($rendererProvider, $menuProvider);
$this->assertSame($child, $helper->get('default', array('child', 'invalid')));
}
public function testRenderMenuByPath()
{
$child = $this->getMock('Knp\Menu\ItemInterface');
$menu = $this->getMock('Knp\Menu\ItemInterface');
$menu->expects($this->any())
->method('getChild')
->with('child')
->will($this->returnValue($child))
;
$renderer = $this->getMock('Knp\Menu\Renderer\RendererInterface');
$renderer->expects($this->once())
->method('render')
->with($child, array())
->will($this->returnValue('foobar
'))
;
$rendererProvider = $this->getMock('Knp\Menu\Renderer\RendererProviderInterface');
$rendererProvider->expects($this->once())
->method('get')
->with(null)
->will($this->returnValue($renderer))
;
$helper = new Helper($rendererProvider);
$this->assertEquals('foobar
', $helper->render(array($menu, 'child')));
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage The array cannot be empty
*/
public function testRenderByEmptyPath()
{
$helper = new Helper($this->getMock('Knp\Menu\Renderer\RendererProviderInterface'));
$helper->render(array());
}
public function testBreadcrumbsArray()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$manipulator = $this->getMock('Knp\Menu\Util\MenuManipulator');
$manipulator->expects($this->any())
->method('getBreadcrumbsArray')
->with($menu)
->will($this->returnValue(array('A', 'B')));
$helper = new Helper($this->getMock('Knp\Menu\Renderer\RendererProviderInterface'), null, $manipulator);
$this->assertEquals(array('A', 'B'), $helper->getBreadcrumbsArray($menu));
}
}
tests/Knp/Menu/Tests/Twig/MenuExtensionTest.php 0000666 00000014244 13052403053 0015534 0 ustar 00 markTestSkipped('Twig is not available');
}
}
public function testRenderMenu()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$helper = $this->getHelperMock(array('render'));
$helper->expects($this->once())
->method('render')
->with($menu, array(), null)
->will($this->returnValue('foobar
'))
;
$this->assertEquals('foobar
', $this->getTemplate('{{ knp_menu_render(menu) }}', $helper)->render(array('menu' => $menu)));
}
public function testRenderMenuWithOptions()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$helper = $this->getHelperMock(array('render'));
$helper->expects($this->once())
->method('render')
->with($menu, array('firstClass' => 'test'), null)
->will($this->returnValue('foobar
'))
;
$this->assertEquals('foobar
', $this->getTemplate('{{ knp_menu_render(menu, {"firstClass": "test"}) }}', $helper)->render(array('menu' => $menu)));
}
public function testRenderMenuWithRenderer()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$helper = $this->getHelperMock(array('render'));
$helper->expects($this->once())
->method('render')
->with($menu, array(), 'custom')
->will($this->returnValue('foobar
'))
;
$this->assertEquals('foobar
', $this->getTemplate('{{ knp_menu_render(menu, {}, "custom") }}', $helper)->render(array('menu' => $menu)));
}
public function testRenderMenuByName()
{
$helper = $this->getHelperMock(array('render'));
$helper->expects($this->once())
->method('render')
->with('default', array(), null)
->will($this->returnValue('foobar
'))
;
$this->assertEquals('foobar
', $this->getTemplate('{{ knp_menu_render(menu) }}', $helper)->render(array('menu' => 'default')));
}
public function testRetrieveMenuByName()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$helper = $this->getHelperMock(array('get', 'render'));
$helper->expects($this->once())
->method('render')
->with($menu, array(), null)
->will($this->returnValue('foobar
'))
;
$helper->expects($this->once())
->method('get')
->with('default')
->will($this->returnValue($menu))
;
$this->assertEquals('foobar
', $this->getTemplate('{{ knp_menu_render(knp_menu_get("default")) }}', $helper)->render(array()));
}
public function testGetBreadcrumbsArray()
{
$helper = $this->getHelperMock(array('getBreadcrumbsArray'));
$helper->expects($this->any())
->method('getBreadcrumbsArray')
->with('default')
->will($this->returnValue(array('A', 'B')))
;
$this->assertEquals('A, B', $this->getTemplate('{{ knp_menu_get_breadcrumbs_array("default")|join(", ") }}', $helper)->render(array()));
}
public function testPathAsString()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$helper = $this->getHelperMock(array('get'));
$manipulator = $this->getManipulatorMock(array('getPathAsString'));
$helper->expects($this->any())
->method('get')
->with('default')
->will($this->returnValue($menu));
$manipulator->expects($this->any())
->method('getPathAsString')
->with($menu)
->will($this->returnValue('A > B'))
;
$this->assertEquals('A > B', $this->getTemplate('{{ knp_menu_get("default")|knp_menu_as_string }}', $helper, null, $manipulator)->render(array()));
}
public function testIsCurrent()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$helper = $this->getHelperMock(array());
$matcher = $this->getMatcherMock();
$matcher->expects($this->any())
->method('isCurrent')
->with($menu)
->will($this->returnValue(true))
;
$this->assertEquals('current', $this->getTemplate('{{ menu is knp_menu_current ? "current" : "not current" }}', $helper, $matcher)->render(array('menu' => $menu)));
}
public function testIsAncestor()
{
$menu = $this->getMock('Knp\Menu\ItemInterface');
$helper = $this->getHelperMock(array());
$matcher = $this->getMatcherMock();
$matcher->expects($this->any())
->method('isAncestor')
->with($menu)
->will($this->returnValue(false))
;
$this->assertEquals('not ancestor', $this->getTemplate('{{ menu is knp_menu_ancestor ? "ancestor" : "not ancestor" }}', $helper, $matcher)->render(array('menu' => $menu)));
}
private function getHelperMock(array $methods)
{
return $this->getMockBuilder('Knp\Menu\Twig\Helper')
->disableOriginalConstructor()
->setMethods($methods)
->getMock()
;
}
private function getManipulatorMock(array $methods)
{
return $this->getMockBuilder('Knp\Menu\Util\MenuManipulator')
->disableOriginalConstructor()
->setMethods($methods)
->getMock()
;
}
private function getMatcherMock()
{
return $this->getMock('Knp\Menu\Matcher\MatcherInterface');
}
/**
* @param string $template
* @param \Knp\Menu\Twig\Helper $helper
*
* @return \Twig_Template
*/
private function getTemplate($template, $helper, $matcher = null, $menuManipulator = null)
{
$loader = new \Twig_Loader_Array(array('index' => $template));
$twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
$twig->addExtension(new MenuExtension($helper, $matcher, $menuManipulator));
return $twig->loadTemplate('index');
}
}
tests/Knp/Menu/Tests/Util/MenuManipulatorTest.php 0000666 00000036235 13052403053 0016062 0 ustar 00 addChild('c1');
$menu->addChild('c2');
$menu->addChild('c3');
$menu->addChild('c4');
$manipulator = new MenuManipulator();
$manipulator->moveToFirstPosition($menu['c3']);
$this->assertEquals(array('c3', 'c1', 'c2', 'c4'), array_keys($menu->getChildren()));
}
public function testMoveToLastPosition()
{
$menu = new MenuItem('root', new MenuFactory());
$menu->addChild('c1');
$menu->addChild('c2');
$menu->addChild('c3');
$menu->addChild('c4');
$manipulator = new MenuManipulator();
$manipulator->moveToLastPosition($menu['c2']);
$this->assertEquals(array('c1', 'c3', 'c4', 'c2'), array_keys($menu->getChildren()));
}
public function testMoveToPosition()
{
$menu = new MenuItem('root', new MenuFactory());
$menu->addChild('c1');
$menu->addChild('c2');
$menu->addChild('c3');
$menu->addChild('c4');
$manipulator = new MenuManipulator();
$manipulator->moveToPosition($menu['c1'], 2);
$this->assertEquals(array('c2', 'c3', 'c1', 'c4'), array_keys($menu->getChildren()));
}
/**
* @dataProvider getSliceData
*/
public function testSlice($offset, $length, $count, $keys)
{
$manipulator = new MenuManipulator();
$sliced = $manipulator->slice($this->pt1, $offset, $length);
$this->assertCount($count, $sliced);
$this->assertEquals($keys, array_keys($sliced->getChildren()));
}
public function getSliceData()
{
$this->setUp();
return array(
'numeric offset and numeric length' => array(0, 2, 2, array($this->ch1->getName(), $this->ch2->getName())),
'numeric offset and no length' => array(0, null, 3, array($this->ch1->getName(), $this->ch2->getName(), $this->ch3->getName())),
'named offset and no length' => array('Child 2', null, 2, array($this->ch2->getName(), $this->ch3->getName())),
'child offset and no length' => array($this->ch3, null, 1, array($this->ch3->getName())),
'numeric offset and named length' => array(0, 'Child 3', 2, array($this->ch1->getName(), $this->ch2->getName())),
'numeric offset and child length' => array(0, $this->ch3, 2, array($this->ch1->getName(), $this->ch2->getName())),
);
}
/**
* @dataProvider getSplitData
*/
public function testSplit($length, $count, $keys)
{
$manipulator = new MenuManipulator();
$splitted = $manipulator->split($this->pt1, $length);
$this->assertArrayHasKey('primary', $splitted);
$this->assertArrayHasKey('secondary', $splitted);
$this->assertCount($count, $splitted['primary']);
$this->assertCount(3 - $count, $splitted['secondary']);
$this->assertEquals($keys, array_keys($splitted['primary']->getChildren()));
}
public function getSplitData()
{
$this->setUp();
return array(
'numeric length' => array(1, 1, array($this->ch1->getName())),
'named length' => array('Child 3', 2, array($this->ch1->getName(), $this->ch2->getName())),
'child length' => array($this->ch3, 2, array($this->ch1->getName(), $this->ch2->getName())),
);
}
public function testPathAsString()
{
$manipulator = new MenuManipulator();
$this->assertEquals('Root li > Parent 2 > Child 4', $manipulator->getPathAsString($this->ch4), 'Path with default separator');
$this->assertEquals('Root li / Parent 1 / Child 2', $manipulator->getPathAsString($this->ch2, ' / '), 'Path with custom separator');
}
public function testBreadcrumbsArray()
{
$manipulator = new MenuManipulator();
$this->menu->addChild('child', array('uri' => 'http://www.symfony-reloaded.org'));
$this->menu->addChild('123', array('uri' => 'http://www.symfony-reloaded.org'));
$this->assertEquals(
array(array('label' => 'Root li', 'uri' => null, 'item' => $this->menu), array('label' => 'Parent 1', 'uri' => null, 'item' => $this->pt1)),
$manipulator->getBreadcrumbsArray($this->pt1)
);
$this->assertEquals(
array(array('label' => 'Root li', 'uri' => null, 'item' => $this->menu), array('label' => 'child', 'uri' => 'http://www.symfony-reloaded.org', 'item' => $this->menu['child'])),
$manipulator->getBreadcrumbsArray($this->menu['child'])
);
$this->assertEquals(
array(
array('label' => 'Root li', 'uri' => null, 'item' => $this->menu),
array('label' => 'child', 'uri' => 'http://www.symfony-reloaded.org', 'item' => $this->menu['child']),
array('label' => 'subitem1', 'uri' => null, 'item' => null),
),
$manipulator->getBreadcrumbsArray($this->menu['child'], 'subitem1')
);
$item = $this->getMock('Knp\Menu\ItemInterface');
$item->expects($this->any())
->method('getLabel')
->will($this->returnValue('mock'));
$item->expects($this->any())
->method('getUri')
->will($this->returnValue('foo'));
$this->assertEquals(
array(
array('label' => 'Root li', 'uri' => null, 'item' => $this->menu),
array('label' => 'child', 'uri' => 'http://www.symfony-reloaded.org', 'item' => $this->menu['child']),
array('label' => 'subitem1', 'uri' => null, 'item' => null),
array('label' => 'subitem2', 'uri' => null, 'item' => null),
array('label' => 'subitem3', 'uri' => 'http://php.net', 'item' => null),
array('label' => 'subitem4', 'uri' => null, 'item' => null),
array('label' => 'mock', 'uri' => 'foo', 'item' => $item),
),
$manipulator->getBreadcrumbsArray($this->menu['child'], array(
'subitem1',
'subitem2' => null,
'subitem3' => 'http://php.net',
array('label' => 'subitem4', 'uri' => null, 'item' => null),
$item,
))
);
$this->assertEquals(
array(array('label' => 'Root li', 'uri' => null, 'item' => $this->menu), array('label' => '123', 'uri' => 'http://www.symfony-reloaded.org', 'item' => $this->menu['123'])),
$manipulator->getBreadcrumbsArray($this->menu['123'])
);
$this->assertEquals(
array(
array('label' => 'Root li', 'uri' => null, 'item' => $this->menu),
array('label' => 'child', 'uri' => 'http://www.symfony-reloaded.org', 'item' => $this->menu['child']),
array('label' => 'mock', 'uri' => 'foo', 'item' => $item),
),
$manipulator->getBreadcrumbsArray($this->menu['child'], $item)
);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testBreadcrumbsArrayInvalidData()
{
$manipulator = new MenuManipulator();
$manipulator->getBreadcrumbsArray($this->pt1, array(new \stdClass()));
}
public function testCallRecursively()
{
$factory = new MenuFactory();
$menu = $factory->createItem('test_menu');
foreach (range(1, 2) as $i) {
$child = $this->getMock('Knp\Menu\ItemInterface');
$child->expects($this->any())
->method('getName')
->will($this->returnValue('Child '.$i))
;
$child->expects($this->once())
->method('setDisplay')
->with(false)
;
$child->expects($this->once())
->method('getChildren')
->will($this->returnValue(array()))
;
$menu->addChild($child);
}
$manipulator = new MenuManipulator();
$manipulator->callRecursively($menu, 'setDisplay', array(false));
$this->assertFalse($menu->isDisplayed());
}
public function testToArrayWithChildren()
{
$menu = $this->createMenu();
$menu->addChild('jack', array('uri' => 'http://php.net', 'linkAttributes' => array('title' => 'php'), 'display' => false))
->addChild('john', array('current' => true))->setCurrent(true)
;
$menu->addChild('joe', array(
'attributes' => array('class' => 'leaf'),
'label' => 'test',
'labelAttributes' => array('class' => 'center'),
'displayChildren' => false,
))->setCurrent(false);
$manipulator = new MenuManipulator();
$this->assertEquals(
array(
'name' => 'test_menu',
'label' => 'test_menu',
'uri' => 'homepage',
'attributes' => array(),
'labelAttributes' => array(),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'extras' => array(),
'display' => true,
'displayChildren' => true,
'current' => null,
'children' => array(
'jack' => array(
'name' => 'jack',
'label' => 'jack',
'uri' => 'http://php.net',
'attributes' => array(),
'labelAttributes' => array(),
'linkAttributes' => array('title' => 'php'),
'childrenAttributes' => array(),
'extras' => array(),
'display' => false,
'displayChildren' => true,
'current' => null,
'children' => array(
'john' => array(
'name' => 'john',
'label' => 'john',
'uri' => null,
'attributes' => array(),
'labelAttributes' => array(),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'extras' => array(),
'display' => true,
'displayChildren' => true,
'children' => array(),
'current' => true,
),
),
),
'joe' => array(
'name' => 'joe',
'label' => 'test',
'uri' => null,
'attributes' => array('class' => 'leaf'),
'labelAttributes' => array('class' => 'center'),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'extras' => array(),
'display' => true,
'displayChildren' => false,
'children' => array(),
'current' => false,
),
),
),
$manipulator->toArray($menu)
);
}
public function testToArrayWithLimitedChildren()
{
$menu = $this->createMenu();
$menu->addChild('jack', array('uri' => 'http://php.net', 'linkAttributes' => array('title' => 'php'), 'display' => false))
->addChild('john')
;
$menu->addChild('joe', array('attributes' => array('class' => 'leaf'), 'label' => 'test', 'labelAttributes' => array('class' => 'center'), 'displayChildren' => false));
$manipulator = new MenuManipulator();
$this->assertEquals(
array(
'name' => 'test_menu',
'label' => 'test_menu',
'uri' => 'homepage',
'attributes' => array(),
'labelAttributes' => array(),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'extras' => array(),
'display' => true,
'displayChildren' => true,
'current' => null,
'children' => array(
'jack' => array(
'name' => 'jack',
'label' => 'jack',
'uri' => 'http://php.net',
'attributes' => array(),
'labelAttributes' => array(),
'linkAttributes' => array('title' => 'php'),
'childrenAttributes' => array(),
'extras' => array(),
'display' => false,
'displayChildren' => true,
'current' => null,
),
'joe' => array(
'name' => 'joe',
'label' => 'test',
'uri' => null,
'attributes' => array('class' => 'leaf'),
'labelAttributes' => array('class' => 'center'),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'extras' => array(),
'display' => true,
'displayChildren' => false,
'current' => null,
),
),
),
$manipulator->toArray($menu, 1)
);
}
public function testToArrayWithoutChildren()
{
$menu = $this->createMenu();
$menu->addChild('jack', array('uri' => 'http://php.net', 'linkAttributes' => array('title' => 'php'), 'display' => false));
$menu->addChild('joe', array('attributes' => array('class' => 'leaf'), 'label' => 'test', 'labelAttributes' => array('class' => 'center'), 'displayChildren' => false));
$manipulator = new MenuManipulator();
$this->assertEquals(
array(
'name' => 'test_menu',
'label' => 'test_menu',
'uri' => 'homepage',
'attributes' => array(),
'labelAttributes' => array(),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'extras' => array(),
'display' => true,
'displayChildren' => true,
'current' => null,
),
$manipulator->toArray($menu, 0)
);
}
/**
* Create a new MenuItem
*
* @param string $name
* @param string $uri
* @param array $attributes
*
* @return \Knp\Menu\MenuItem
*/
private function createMenu($name = 'test_menu', $uri = 'homepage', array $attributes = array())
{
$factory = new MenuFactory();
return $factory->createItem($name, array('attributes' => $attributes, 'uri' => $uri));
}
}