1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?php
/**
* Composer Magento Installer
*/
namespace MagentoHackathon\Composer\Magento\Deploystrategy;
/**
* Symlink deploy strategy
*/
class Symlink extends DeploystrategyAbstract
{
/**
* Creates a symlink with lots of error-checking
*
* @param string $source
* @param string $dest
* @return bool
* @throws \ErrorException
*/
public function createDelegate($source, $dest)
{
$sourcePath = $this->getSourceDir() . '/' . $this->removeTrailingSlash($source);
$destPath = $this->getDestDir() . '/' . $this->removeTrailingSlash($dest);
if (!is_file($sourcePath) && !is_dir($sourcePath)) {
throw new \ErrorException("Could not find path '$sourcePath'");
}
/*
Assume app/etc exists, app/etc/a does not exist unless specified differently
OK dir app/etc/a --> link app/etc/a to dir
OK dir app/etc/ --> link app/etc/dir to dir
OK dir app/etc --> link app/etc/dir to dir
OK dir/* app/etc --> for each dir/$file create a target link in app/etc
OK dir/* app/etc/ --> for each dir/$file create a target link in app/etc
OK dir/* app/etc/a --> for each dir/$file create a target link in app/etc/a
OK dir/* app/etc/a/ --> for each dir/$file create a target link in app/etc/a
OK file app/etc --> link app/etc/file to file
OK file app/etc/ --> link app/etc/file to file
OK file app/etc/a --> link app/etc/a to file
OK file app/etc/a --> if app/etc/a is a file throw exception unless force is set, in that case rm and see above
OK file app/etc/a/ --> link app/etc/a/file to file regardless if app/etc/a existst or not
*/
// Symlink already exists
if (is_link($destPath)) {
if (realpath(readlink($destPath)) == realpath($sourcePath)) {
// .. and is equal to current source-link
return true;
}
unlink($destPath);
}
// Create all directories up to one below the target if they don't exist
$destDir = dirname($destPath);
if (!file_exists($destDir)) {
mkdir($destDir, 0777, true);
}
// Handle source to dir linking,
// e.g. Namespace_Module.csv => app/locale/de_DE/
// Namespace/ModuleDir => Namespace/
// Namespace/ModuleDir => Namespace/, but Namespace/ModuleDir may exist
// Namespace/ModuleDir => Namespace/ModuleDir, but ModuleDir may exist
if (file_exists($destPath) && is_dir($destPath)) {
if (basename($sourcePath) === basename($destPath)) {
if ($this->isForced()) {
$this->rmdirRecursive($destPath);
} else {
throw new \ErrorException("Target $dest already exists (set extra.magento-force to override)");
}
} else {
$destPath .= '/' . basename($source);
}
return $this->create($source, substr($destPath, strlen($this->getDestDir())+1));
}
// From now on $destPath can't be a directory, that case is already handled
// If file exists and force is not specified, throw exception unless FORCE is set
// existing symlinks are already handled
if (file_exists($destPath)) {
if ($this->isForced()) {
unlink($destPath);
} else {
throw new \ErrorException("Target $dest already exists and is not a symlink (set extra.magento-force to override)");
}
}
// Windows doesn't allow relative symlinks
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
$sourcePath = $this->getRelativePath($destPath, $sourcePath);
}
// Create symlink
if(false === symlink($sourcePath, $destPath)) {
throw new \ErrorException("An error occured while creating symlink" . $sourcePath);
}
// Check we where able to create the symlink
if(false === $destPath = readlink($destPath)){
throw new \ErrorException("Symlink $destPath points to target $destPath");
}
return true;
}
/**
* Returns the relative path from $from to $to
* This is utility method for symlink creation.
*
* @param string $from
* @param string $to
*
* @return string
*/
public function getRelativePath($from, $to)
{
$from = str_replace(array('/./', '//', '\\'), '/', $from);
$to = str_replace(array('/./', '//', '\\'), '/', $to);
if (is_file($from)) {
$from = dirname($from);
} else {
$from = rtrim($from, '/');
}
$dir = explode('/', $from);
$file = explode('/', $to);
while ($file && $dir && ($dir[0] == $file[0])) {
array_shift($file);
array_shift($dir);
}
$relativePath = str_repeat('../', count($dir)) . implode('/', $file);
return $relativePath;
}
}