Completed
Push — try/use_composer_extra_replace... ( acb669...9a935a )
by
unknown
27:59 queued 21:19
created

customize_textdomain_in_dir()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
/**
3
 * The Textdomain_Customizer class.
4
 *
5
 * @package jetpack-config
6
 */
7
8
// phpcs:disable WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
9
// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
10
11
namespace Automattic\Jetpack\Config;
12
13
use Composer\Script\Event;
14
15
/**
16
 * This class is used to customize the textdomain of the strings in the packages.
17
 */
18
class Textdomain_Customizer {
19
20
	/**
21
	 * The Composer object.
22
	 *
23
	 * @var Composer
24
	 */
25
	private $composer;
26
27
	/**
28
	 * The textdomain.
29
	 *
30
	 * @var string
31
	 */
32
	private $textdomain;
33
34
	/**
35
	 * The vendor directory.
36
	 *
37
	 * @var string
38
	 */
39
	private $vendor_dir;
40
41
	/**
42
	 * The constructor.
43
	 *
44
	 * @param Composer $composer a Composer object.
45
	 */
46
	public function __construct( $composer ) {
47
		$this->composer = $composer;
48
	}
49
50
	/**
51
	 * This method is called when composer fires the post_autoload_dump event.
52
	 *
53
	 * @param Event $event The composer event.
54
	 */
55
	public static function post_autoload_dump( Event $event ) {
56
		$composer = $event->getComposer();
57
58
		$instance = ( new self( $composer ) );
59
60
		/*
61
		 * Check the version of the Config package and bail if it's a dev version.
62
		 * We don't want to change files during development.
63
		 */
64
		$package = $instance->find_package( 'automattic/jetpack-config' );
65
		if ( $package->isDev() ) {
66
			return;
67
		}
68
69
		$instance->customize_textdomain_in_packages();
70
	}
71
72
	/**
73
	 * Find a package in the local composer repo with the input package name. No
74
	 * verison constraints are used to find the package.
75
	 *
76
	 * @param string $package_name The package name.
77
	 *
78
	 * @return Composer\Package|null The package object.
79
	 */
80
	private function find_package( $package_name ) {
81
		$local_repo = $this->composer->getRepositoryManager()->getLocalRepository();
82
		return $local_repo->findPackage( $package_name, '*' );
83
	}
84
85
	/**
86
	 * Traverses the project's packages. Sets the textdomain in the Jetpack
87
	 * packages that declare translatable files in their composer.json files.
88
	 */
89
	public function customize_textdomain_in_packages() {
90
		$packages = $this->get_packages();
91
92
		if ( ! is_array( $packages ) ) {
93
			return;
94
		}
95
96
		$this->set_vendor_dir();
97
		$this->set_textdomain();
98
99
		foreach ( $packages as $package ) {
100
			$jetpack_package_prefix = 'automattic/jetpack-';
101
			$current_package_prefix = substr( $package->getName(), 0, strlen( $jetpack_package_prefix ) );
102
103
			if ( $jetpack_package_prefix !== $current_package_prefix ) {
104
				// Not a Jetpack package, so skip.
105
				continue;
106
			}
107
108
			if ( $package->getExtra() && isset( $package->getExtra()['translatable'] ) ) {
109
				$files = (array) $package->getExtra()['translatable'];
110
				$this->customize_textdomain_in_files( $files );
111
			}
112
		}
113
	}
114
115
	/**
116
	 * Gets the packages in the local composer repository.
117
	 *
118
	 * @return array of Composer\Package\PackagesInterface objects.
119
	 */
120
	protected function get_packages() {
121
		$local_repo = $this->composer->getRepositoryManager()->getLocalRepository();
122
		return $local_repo->getPackages();
123
	}
124
125
	/**
126
	 * Sets the $vendor_dir instance variable using the vendor directory from
127
	 * the composer config.
128
	 */
129
	protected function set_vendor_dir() {
130
		$vendor_dir       = $this->composer->getConfig()->get( 'vendor-dir' );
131
		$this->vendor_dir = rtrim( $vendor_dir, '/' ) . '/';
132
	}
133
134
	/**
135
	 * Sets the textdomain instance variable using the textdomain provided by
136
	 * the plugin. If the plugin did not provide a textdomain, uses 'default'.
137
	 */
138
	private function set_textdomain() {
139
		$root_extra = $this->get_root_extra();
140
141
		if ( isset( $root_extra['textdomain'] ) &&
142
			is_string( $root_extra['textdomain'] ) ) {
143
144
				$this->textdomain = $root_extra['textdomain'];
145
				return;
146
		}
147
148
		$this->textdomain = 'default';
149
	}
150
151
	/**
152
	 * Returns the value of the extra section in the root packages's
153
	 * composer.json file.
154
	 *
155
	 * @return array mixed The root package's extra array.
156
	 */
157
	protected function get_root_extra() {
158
		return $this->composer->getPackage()->getExtra();
159
	}
160
161
	/**
162
	 * Sets the textdomain in the input files and directories.
163
	 *
164
	 * @param array $files The array of translatable files.
165
	 */
166
	private function customize_textdomain_in_files( $files ) {
167
		if ( ! is_array( $files ) ) {
168
			return;
169
		}
170
171
		foreach ( $files as $file ) {
172
			$file_path = realpath( $this->vendor_dir . $file );
173
174
			if ( ! $file_path ) {
175
				return;
176
			}
177
178
			if ( is_dir( $file_path ) ) {
179
				$this->customize_textdomain_in_dir( $file_path );
180
			} else {
181
				$this->customize_textdomain_in_file( $file_path );
182
			}
183
		}
184
	}
185
186
	/**
187
	 * Recursively traverses the input directory and sets the textdomain in
188
	 * all of the files.
189
	 *
190
	 * @param string $dir The path to the directory.
191
	 */
192
	private function customize_textdomain_in_dir( $dir ) {
193
		$file_path = realpath( $dir );
194
195
		if ( $file_path ) {
196
			return;
197
		}
198
199
		$iterator = new \RecursiveDirectoryIterator(
200
			$file_path,
201
			\RecursiveDirectoryIterator::SKIP_DOTS
202
		);
203
204
		foreach ( new \RecursiveIteratorIterator( $iterator ) as $file_info ) {
205
				$this->customize_textdomain_in_file( $file_info->getRealPath() );
206
		}
207
	}
208
209
	/**
210
	 * Replaces all occurrences of the placeholder textdomain JETPACK_CUSTOMIZE_TEXTDOMAIN
211
	 * in the input file with the plugin's textdomain.
212
	 *
213
	 * @param string $file_path The file path.
214
	 */
215
	private function customize_textdomain_in_file( $file_path ) {
216
		$file_path = realpath( $file_path );
217
		if ( ! $file_path ) {
218
			return;
219
		}
220
221
		$file_contents = file_get_contents( $file_path );
222
223
		$file_contents = str_replace(
224
			'JETPACK_CUSTOMIZE_TEXTDOMAIN',
225
			'\'' . $this->textdomain . '\'',
226
			$file_contents
227
		);
228
229
		file_put_contents( $file_path, $file_contents );
230
	}
231
}
232