11
11
12
12
namespace Symfony \Bundle \FrameworkBundle \Command ;
13
13
14
+ use Symfony \Component \Console \Helper \Table ;
14
15
use Symfony \Component \Console \Input \InputArgument ;
15
16
use Symfony \Component \Console \Input \InputOption ;
16
17
use Symfony \Component \Console \Input \InputInterface ;
17
18
use Symfony \Component \Console \Output \OutputInterface ;
19
+ use Symfony \Component \Console \Style \SymfonyStyle ;
18
20
use Symfony \Component \Filesystem \Exception \IOException ;
21
+ use Symfony \Component \Filesystem \Filesystem ;
19
22
use Symfony \Component \Finder \Finder ;
23
+ use Symfony \Component \HttpKernel \Bundle \BundleInterface ;
20
24
21
25
/**
22
26
* Command that places bundle web assets into a given directory.
23
27
*
24
28
* @author Fabien Potencier <fabien@symfony.com>
29
+ * @author Gábor Egyed <gabor.egyed@gmail.com>
25
30
*/
26
31
class AssetsInstallCommand extends ContainerAwareCommand
27
32
{
33
+ const METHOD_COPY = 'copy ' ;
34
+ const METHOD_ABSOLUTE_SYMLINK = 'absolute symlink ' ;
35
+ const METHOD_RELATIVE_SYMLINK = 'relative symlink ' ;
36
+
37
+ /**
38
+ * @var Filesystem
39
+ */
40
+ private $ filesystem ;
41
+
28
42
/**
29
43
* {@inheritdoc}
30
44
*/
@@ -63,8 +77,6 @@ protected function configure()
63
77
64
78
/**
65
79
* {@inheritdoc}
66
- *
67
- * @throws \InvalidArgumentException When the target directory does not exist or symlink cannot be used
68
80
*/
69
81
protected function execute (InputInterface $ input , OutputInterface $ output )
70
82
{
@@ -74,77 +86,164 @@ protected function execute(InputInterface $input, OutputInterface $output)
74
86
throw new \InvalidArgumentException (sprintf ('The target directory "%s" does not exist. ' , $ input ->getArgument ('target ' )));
75
87
}
76
88
77
- $ filesystem = $ this ->getContainer ()->get ('filesystem ' );
89
+ $ this -> filesystem = $ this ->getContainer ()->get ('filesystem ' );
78
90
79
91
// Create the bundles directory otherwise symlink will fail.
80
92
$ bundlesDir = $ targetArg .'/bundles/ ' ;
81
- $ filesystem ->mkdir ($ bundlesDir , 0777 );
93
+ $ this -> filesystem ->mkdir ($ bundlesDir , 0777 );
82
94
83
- // relative implies symlink
84
- $ symlink = $ input -> getOption ( ' symlink ' ) || $ input -> getOption ( ' relative ' );
95
+ $ io = new SymfonyStyle ( $ input , $ output );
96
+ $ io -> newLine ( );
85
97
86
- if ($ symlink ) {
87
- $ output ->writeln ('Trying to install assets as <comment>symbolic links</comment>. ' );
98
+ if ($ input ->getOption ('relative ' )) {
99
+ $ expectedMethod = self ::METHOD_RELATIVE_SYMLINK ;
100
+ $ io ->text ('Trying to install assets as <info>relative symbolic links</info>. ' );
101
+ } elseif ($ input ->getOption ('symlink ' )) {
102
+ $ expectedMethod = self ::METHOD_ABSOLUTE_SYMLINK ;
103
+ $ io ->text ('Trying to install assets as <info>absolute symbolic links</info>. ' );
88
104
} else {
89
- $ output->writeln ('Installing assets as <comment>hard copies</comment>. ' );
105
+ $ expectedMethod = self ::METHOD_COPY ;
106
+ $ io ->text ('Installing assets as <info>hard copies</info>. ' );
90
107
}
91
108
109
+ $ io ->newLine ();
110
+
111
+ $ rows = array ();
112
+ $ copyUsed = false ;
113
+ $ exitCode = 0 ;
114
+ /** @var BundleInterface $bundle */
92
115
foreach ($ this ->getContainer ()->get ('kernel ' )->getBundles () as $ bundle ) {
93
- if (is_dir ($ originDir = $ bundle ->getPath ().'/Resources/public ' )) {
94
- $ targetDir = $ bundlesDir .preg_replace ('/bundle$/ ' , '' , strtolower ($ bundle ->getName ()));
95
-
96
- $ output ->writeln (sprintf ('Installing assets for <comment>%s</comment> into <comment>%s</comment> ' , $ bundle ->getNamespace (), $ targetDir ));
97
-
98
- $ filesystem ->remove ($ targetDir );
99
-
100
- if ($ symlink ) {
101
- if ($ input ->getOption ('relative ' )) {
102
- $ relativeOriginDir = $ filesystem ->makePathRelative ($ originDir , realpath ($ bundlesDir ));
103
- } else {
104
- $ relativeOriginDir = $ originDir ;
105
- }
106
-
107
- try {
108
- $ filesystem ->symlink ($ relativeOriginDir , $ targetDir );
109
- if (!file_exists ($ targetDir )) {
110
- throw new IOException ('Symbolic link is broken ' );
111
- }
112
- $ output ->writeln ('The assets were installed using symbolic links. ' );
113
- } catch (IOException $ e ) {
114
- if (!$ input ->getOption ('relative ' )) {
115
- $ this ->hardCopy ($ originDir , $ targetDir );
116
- $ output ->writeln ('It looks like your system doesn \'t support symbolic links, so the assets were installed by copying them. ' );
117
- }
118
-
119
- // try again without the relative option
120
- try {
121
- $ filesystem ->symlink ($ originDir , $ targetDir );
122
- if (!file_exists ($ targetDir )) {
123
- throw new IOException ('Symbolic link is broken ' );
124
- }
125
- $ output ->writeln ('It looks like your system doesn \'t support relative symbolic links, so the assets were installed by using absolute symbolic links. ' );
126
- } catch (IOException $ e ) {
127
- $ this ->hardCopy ($ originDir , $ targetDir );
128
- $ output ->writeln ('It looks like your system doesn \'t support symbolic links, so the assets were installed by copying them. ' );
129
- }
130
- }
116
+ if (!is_dir ($ originDir = $ bundle ->getPath ().'/Resources/public ' )) {
117
+ continue ;
118
+ }
119
+
120
+ $ targetDir = $ bundlesDir .preg_replace ('/bundle$/ ' , '' , strtolower ($ bundle ->getName ()));
121
+
122
+ if (OutputInterface::VERBOSITY_VERBOSE <= $ output ->getVerbosity ()) {
123
+ $ message = sprintf ("%s \n-> %s " , $ bundle ->getName (), $ targetDir );
124
+ } else {
125
+ $ message = $ bundle ->getName ();
126
+ }
127
+
128
+ try {
129
+ $ this ->filesystem ->remove ($ targetDir );
130
+
131
+ if (self ::METHOD_RELATIVE_SYMLINK === $ expectedMethod ) {
132
+ $ method = $ this ->relativeSymlinkWithFallback ($ originDir , $ targetDir );
133
+ } elseif (self ::METHOD_ABSOLUTE_SYMLINK === $ expectedMethod ) {
134
+ $ method = $ this ->absoluteSymlinkWithFallback ($ originDir , $ targetDir );
131
135
} else {
132
- $ this ->hardCopy ($ originDir , $ targetDir );
136
+ $ method = $ this ->hardCopy ($ originDir , $ targetDir );
133
137
}
138
+
139
+ if (self ::METHOD_COPY === $ method ) {
140
+ $ copyUsed = true ;
141
+ }
142
+
143
+ if ($ method === $ expectedMethod ) {
144
+ $ rows [] = array (sprintf ('<fg=green;options=bold>%s</> ' , '\\' === DIRECTORY_SEPARATOR ? 'OK ' : "\xE2\x9C\x94" /* HEAVY CHECK MARK (U+2714) */ ), $ message , $ method );
145
+ } else {
146
+ $ rows [] = array (sprintf ('<fg=yellow;options=bold>%s</> ' , '\\' === DIRECTORY_SEPARATOR ? 'WARNING ' : '! ' ), $ message , $ method );
147
+ }
148
+ } catch (\Exception $ e ) {
149
+ $ exitCode = 1 ;
150
+ $ rows [] =
10000
array (sprintf ('<fg=red;options=bold>%s</> ' , '\\' === DIRECTORY_SEPARATOR ? 'ERROR ' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */ ), $ message , $ e ->getMessage ());
151
+ }
152
+ }
153
+
154
+ $ io ->table (array ('' , 'Bundle ' , 'Method / Error ' ), $ rows );
155
+
156
+ if (0 !== $ exitCode ) {
157
+ $ io ->error ('Some errors occurred while installing assets. ' );
158
+ } else {
159
+ if ($ copyUsed ) {
160
+ $ io ->note ('Some assets were installed via copy. If you make changes to these assets you have to run this command again. ' );
134
161
}
162
+ $ io ->success ('All assets were successfully installed. ' );
135
163
}
164
+
165
+ return $ exitCode ;
136
166
}
137
167
138
168
/**
169
+ * Try to create relative symlink.
170
+ *
171
+ * Falling back to absolute symlink and finally hard copy.
172
+ *
139
173
* @param string $originDir
140
174
* @param string $targetDir
175
+ *
176
+ * @return string
141
177
*/
142
- private function hardCopy ($ originDir , $ targetDir )
178
+ private function relativeSymlinkWithFallback ($ originDir , $ targetDir )
179
+ {
180
+ try {
181
+ $ this ->symlink ($ originDir , $ targetDir , true );
182
+ $ method = self ::METHOD_RELATIVE_SYMLINK ;
183
+ } catch (IOException $ e ) {
184
+ $ method = $ this ->absoluteSymlinkWithFallback ($ originDir , $ targetDir );
185
+ }
186
+
187
+ return $ method ;
188
+ }
189
+
190
+ /**
191
+ * Try to create absolute symlink.
192
+ *
193
+ * Falling back to hard copy.
194
+ *
195
+ * @param string $originDir
196
+ * @param string $targetDir
197
+ *
198
+ * @return string
199
+ */
200
+ private function absoluteSymlinkWithFallback ($ originDir , $ targetDir )
143
201
{
144
- $ filesystem = $ this ->getContainer ()->get ('filesystem ' );
202
+ try {
203
+ $ this ->symlink ($ originDir , $ targetDir );
204
+ $ method = self ::METHOD_ABSOLUTE_SYMLINK ;
205
+ } catch (IOException $ e ) {
206
+ // fall back to copy
207
+ $ method = $ this ->hardCopy ($ originDir , $ targetDir );
208
+ }
209
+
210
+ return $ method ;
211
+ }
145
212
146
- $ filesystem ->mkdir ($ targetDir , 0777 );
213
+ /**
214
+ * Creates symbolic link.
215
+ *
216
+ * @param string $originDir
217
+ * @param string $targetDir
218
+ * @param bool $relative
219
+ *
220
+ * @throws IOException If link can not be created.
221
+ */
222
+ private function symlink ($ originDir , $ targetDir , $ relative = false )
223
+ {
224
+ if ($ relative ) {
225
+ $ originDir = $ this ->filesystem ->makePathRelative ($ originDir , realpath (dirname ($ targetDir )));
226
+ }
227
+ $ this ->filesystem ->symlink ($ originDir , $ targetDir );
228
+ if (!file_exists ($ targetDir )) {
229
+ throw new IOException (sprintf ('Symbolic link "%s" is created but appears to be broken. ' , $ targetDir ), 0 , null , $ targetDir );
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Copies origin to target.
235
+ *
236
+ * @param string $originDir
237
+ * @param string $targetDir
238
+ *
239
+ * @return string
240
+ */
241
+ private function hardCopy ($ originDir , $ targetDir )
242
+ {
243
+ $ this ->filesystem ->mkdir ($ targetDir , 0777 );
147
244
// We use a custom iterator to ignore VCS files
148
- $ filesystem ->mirror ($ originDir , $ targetDir , Finder::create ()->ignoreDotFiles (false )->in ($ originDir ));
245
+ $ this ->filesystem ->mirror ($ originDir , $ targetDir , Finder::create ()->ignoreDotFiles (false )->in ($ originDir ));
246
+
247
+ return self ::METHOD_COPY ;
149
248
}
150
249
}
0 commit comments