85

In a Symfony2 app's routing configuration, I can refer to a file like this:

somepage:
    prefix: someprefix
    resource: "@SomeBundle/Resources/config/config.yml"

Is there any way to access a file relative to the bundle within a controller or other PHP code? In particular, I'm trying to use a Symfony\Component\Yaml\Parser object to parse a file, and I don't want to refer to that file absolutely. Essentially, I want to do this:

$parser = new Parser();
$config = $parser->parse( file_get_contents("@SomeBundle/Resources/config/config.yml") );

I've checked out the Symfony\Component\Finder\Finder class, but I don't think that's what I'm looking for. Any ideas? Or maybe I'm completely overlooking a better way of doing this?

Thomas Kelley
  • 10,187
  • 1
  • 36
  • 43

4 Answers4

180

As a matter of fact, there is a service you could use for this, the kernel ($this->get('kernel')). It has a method called locateResource().

For example:

$kernel = $container->getService('kernel');
$path = $kernel->locateResource('@AdmeDemoBundle/path/to/file/Foo.txt');
kgilden
  • 10,336
  • 3
  • 50
  • 48
  • 5
    Exactly what I was looking for! $this->get('kernel')->locateResource("@SomeBundle/Resources/config/config.yml"); // worked perfectly – Thomas Kelley Sep 28 '11 at 23:15
  • 9
    @tomtheman5: Be sure to catch its exceptions. This method throws `\InvalidArgumentException`, if the file cannot be found or the name is not valid, and `\RuntimeException`, if the name contains invalid/unsafe characters. – kgilden Sep 28 '11 at 23:32
  • 2
    Would love to give you more than just +1. – flu Mar 07 '12 at 10:24
  • How (if at all) would you use this method inside of a doctrine entity, which (I don't think) would (or should) have access to the kernel? – Michael.Lumley Aug 10 '14 at 12:00
  • 1
    @Morslamina Check out fazy's answer. – Tek Dec 29 '14 at 16:36
  • Is there a way to retrieve only the directory location? For eg. `@AcmeDemoBundle/path/to/directory` or will I have to locate a file and then strip the filename? – aalaap Feb 02 '17 at 08:31
81

Thomas Kelley's answer is good (and works!) but if you are using dependency injection and/or don't want to tie your code directly to the kernel, you're better off using the FileLocator class/service:

$fileLocator = $container->get('file_locator');
$path = $fileLocator->locate('@MyBundle/path/to/file.txt')

$fileLocator will be an instance of \Symfony\Component\HttpKernel\Config\FileLocator. $path will be the full, absolute path to the file.

Even though the file_locator service itself uses the kernel, it's a much smaller dependency (easier to substitute for your own implementation, use test doubles, etc.)

To use it with dependency injection:

# services.yml

services:
    my_bundle.my_class:
        class: MyNamespace\MyClass
        arguments:
            - @file_locator

# MyClass.php

use Symfony\Component\Config\FileLocatorInterface as FileLocator;

class MyClass
{
    private $fileLocator;

    public function __construct(FileLocator $fileLocator)
    {
        $this->fileLocator = $fileLocator;
    }

    public function myMethod()
    {
        $path = $this->fileLocator->locate('@MyBundle/path/to/file.txt')
    }
}
Nicholas Shanks
  • 10,623
  • 4
  • 56
  • 80
fazy
  • 2,095
  • 18
  • 30
  • Nice point, but FileLocator gets KernelInterface injected, so basically, you do nothing better if you inject FileLocator instead of Kernel :) – tomazahlin Oct 31 '14 at 16:54
  • 11
    I see your point, and I thought of that. However, the problem is not "what code is loaded/executed?", but "what code does `MyClass` depend on?". The current `FileLocator` implementation uses `KernelInterface`, but if that changes in future `MyClass` doesn't need to know about it. Also, it's now clearer what `MyClass` really needs (and less temptation to couple it with other kernel features). Having said all that, the constructor should probably require `Symfony\Component\Config\FileLocatorInterface` rather than `Symfony\Component\HttpKernel\Config\FileLocator`. Then you can to write your own. – fazy Oct 31 '14 at 17:48
  • 3
    this is the better answer – NDM Dec 02 '14 at 10:03
6

You can use $container->getParameter('kernel.root_dir') to get the app folder of your application, and browse your directories to the file you want.

Reuven
  • 3,336
  • 2
  • 23
  • 29
  • Bundle can be in any directory. Usual examples: `src/Acme/Bundle/MyBundle`, `src/Acme/MyBundle`, `vendor/acme/my-bundle/src` and so on. – Marius Balčytis Nov 29 '15 at 15:20
4

If you want to do that in a file located in src/.../SomeBundle/... you can use __DIR__ to get the full path of the current file. Then append your Resources/... path to that like

$foo = __DIR__.'/Resources/config/config.yml';
prehfeldt
  • 2,184
  • 2
  • 28
  • 48
  • @ruudy "Only registered users with a paid plan can get full access to rule descriptions. This page shows a sample of what you can get by subscribing." – prehfeldt Apr 07 '15 at 07:29
  • i didnt paid and i can see the full description and the explanation of how to solve it. Maybe you only need to register. – ruudy Apr 07 '15 at 13:55
  • Now that I'm registered I can see the page. First of all it says "should not" and not "must not". Secondly imho this is only important if you plan to release your code to the public or plan to override the resource path later. In my closed source projects this won't happen, so this is not that important for me. It depends on the context where you're going to use it, if this point is important for you. Nevertheless thanks for the hint. – prehfeldt Apr 07 '15 at 15:33
  • If you follow the recommendations, you can mock the path, or play with it with testing purposes. Im not english native, is better not use this php constants as posible, much less on a framework like Symfony2. – ruudy Apr 07 '15 at 22:21