Completed
Push — master ( 453641...f48fae )
by Bernhard
02:10
created

DiscoveryUrlGenerator::generateUrlForBinding()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 33
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 33
ccs 11
cts 11
cp 1
rs 8.8571
cc 2
eloc 11
nc 2
nop 2
crap 2
1
<?php
2
3
/*
4
 * This file is part of the puli/url-generator package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Puli\UrlGenerator;
13
14
use InvalidArgumentException;
15
use Puli\Discovery\Api\Discovery;
16
use Puli\Discovery\Binding\ResourceBinding;
17
use Puli\UrlGenerator\Api\CannotGenerateUrlException;
18
use Puli\UrlGenerator\Api\UrlGenerator;
19
use Webmozart\Glob\Glob;
20
use Webmozart\PathUtil\Url;
21
22
/**
23
 * A resource URL generator that uses a {@link ResourceDiscovery} as backend.
24
 *
25
 * @since  1.0
26
 *
27
 * @author Bernhard Schussek <[email protected]>
28
 */
29
class DiscoveryUrlGenerator implements UrlGenerator
30
{
31
    /**
32
     * The binding type of public resources.
33
     */
34
    const BINDING_TYPE = 'puli/public-resource';
35
36
    /**
37
     * The binding parameter used for the server name.
38
     */
39
    const SERVER_PARAMETER = 'server';
40
41
    /**
42
     * The binding parameter used for the public path.
43
     */
44
    const PATH_PARAMETER = 'path';
45
46
    /**
47
     * @var Discovery
48
     */
49
    private $discovery;
50
51
    /**
52
     * @var string[]
53
     */
54
    private $urlFormats;
55
56
    /**
57
     * Creates the URL generator.
58
     *
59
     * @param Discovery $discovery  The resource discovery.
60
     * @param string[]  $urlFormats The URL formats indexed by the server names.
61
     */
62 8
    public function __construct(Discovery $discovery, array $urlFormats)
63
    {
64 8
        $this->discovery = $discovery;
65 8
        $this->urlFormats = $urlFormats;
66 8
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 10
    public function generateUrl($repositoryPath, $currentUrl = null)
72
    {
73 10
        $matchedBinding = null;
74 10
        $bindings = $this->discovery->findBindings(self::BINDING_TYPE);
75
76 10
        foreach ($bindings as $binding) {
77
            /** @var ResourceBinding $binding */
78 9
            if (Glob::match($repositoryPath, $binding->getQuery())) {
79 9
                $matchedBinding = $binding;
80 9
                break;
81
            }
82 10
        }
83
84 10
        if (null === $matchedBinding) {
85 1
            throw new CannotGenerateUrlException(sprintf(
86 1
                'Cannot generate URL for "%s". The path is not public.',
87
                $repositoryPath
88 1
            ));
89
        }
90
91
        // We can't prevent a resource to be mapped to more than one public path
92
        // For now, we'll just take the first one and make the user responsible
93
        // for preventing duplicates
94 9
        $url = $this->generateUrlForBinding($matchedBinding, $repositoryPath);
95
96 8
        if ($currentUrl) {
97
            try {
98 2
                $url = Url::makeRelative($url, $currentUrl);
99 2
            } catch (InvalidArgumentException $e) {
100 1
                throw new CannotGenerateUrlException(sprintf(
101 1
                    'Cannot generate URL for "%s" to current url "%s".',
102 1
                    $repositoryPath, $currentUrl
103 1
                ), $e->getCode(), $e);
104
            }
105 1
        }
106
107 7
        return $url;
108
    }
109
110 7
    private function generateUrlForBinding(ResourceBinding $binding, $repositoryPath)
111
    {
112 7
        $serverName = $binding->getParameterValue(self::SERVER_PARAMETER);
113
114 7
        if (!isset($this->urlFormats[$serverName])) {
115 1
            throw new CannotGenerateUrlException(sprintf(
116 1
                'The server "%s" mapped for path "%s" does not exist.',
117 1
                $serverName,
118
                $repositoryPath
119 1
            ));
120
        }
121
122 6
        $repoBasePath = Glob::getStaticPrefix($binding->getQuery());
123 6
        $serverBasePath = trim($binding->getParameterValue(self::PATH_PARAMETER), '/');
124
125
        // The server path is generated by replacing the base repository path
126
        // (= the path of the binding) by the stored server base path in the
127
        // repository path of the resource.
128
        //
129
        // Example:
130
        //
131
        // resource path: /acme/blog/public/css/style.css
132
        // binding path: /acme/blog/public{,/**/*}
133
        // repo base path: /acme/blog/public
134
        // server base path: /blog
135
        //
136
        // final server path: /blog/css/style.css
137
138 6
        $serverPath = substr_replace($repositoryPath, $serverBasePath, 0, strlen($repoBasePath));
139
140
        // The server path is inserted into the "%s" parameter of the URL format
141 6
        return sprintf($this->urlFormats[$serverName], ltrim($serverPath, '/'));
142
    }
143
}
144