Skip to content

Commit 72182d9

Browse files
authored
[WIP] Update readme (#87)
* update readme * Misc suggested edits * Two edits * Small fix * Small fix 2
1 parent 557dce1 commit 72182d9

File tree

1 file changed

+218
-21
lines changed

1 file changed

+218
-21
lines changed

README.md

Lines changed: 218 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,56 @@
88
[![Slack](https://img.shields.io/badge/slack-%23humbug-red.svg?style=flat-square)](https://symfony.com/slack-invite)
99
[![License](https://img.shields.io/badge/license-MIT-red.svg?style=flat-square)](LICENSE)
1010

11-
PHP-Scoper is a tool for adding a prefix to all PHP namespaces in a given file
12-
or directory.
13-
11+
PHP-Scoper is a tool which essentially moves any body of code, including all
12+
dependencies such as vendor directories, to a new and distinct namespace.
1413

1514
## Goal
1615

17-
PHP-Scoper's goal is to make sure that all code in a directory lies in a
18-
distinct PHP namespace. This is necessary when building PHARs that
16+
PHP-Scoper's goal is to make sure that all code for a project lies in a
17+
distinct PHP namespace. This is necessary, for example, when building PHARs that:
18+
19+
* Bundle their own vendor dependencies; and
20+
* Load/execute code from arbitrary PHP projects with similar dependencies
1921

20-
* Bundle their own vendor dependencies
21-
* Load code of arbitrary PHP projects
22+
When a package (of possibly different versions) exists, and is found in both a PHAR
23+
and the executed code, the one from the PHAR will be used. This means these
24+
PHARs run the risk of raising conflicts between their bundled vendors and the
25+
vendors of the project they are interacting with, leading to issues that are
26+
potentially very difficult to debug due to dissimilar or unsupported package versions.
2227

23-
These PHARs run the risk of raising conflicts between their bundled vendors and
24-
the vendors of the loaded project, if the vendors are required in incompatible
25-
versions.
28+
## Table of Contents
2629

30+
- [Installation](#installation)
31+
- [Usage](#usage)
32+
- [Patchers](#patchers)
33+
- [Global Namespace Whitelisting](#global-namespace-whitelisting)
34+
- [Contributing](#contributing)
35+
- [Building A Scoped PHAR](#building-a-scoped-phar)
36+
- [Credits](#credits)
2737

2838
## Installation
2939

40+
### PHAR (preferred)
41+
42+
The preferred method of installation is to use the PHP-Scoper PHAR, which can
43+
be downloaded from the most recent [Github Release](releases). Subsequent updates
44+
can be downloaded by running:
45+
46+
```bash
47+
php-scoper.phar self-update
48+
```
49+
50+
As the PHAR is signed, you should also download the matching
51+
`php-scoper.phar.pubkey` to the same location. If you rename `php-scoper.phar`
52+
to `php-scoper`, you should also rename `php-scoper.phar.pubkey` to
53+
`php-scoper.pubkey`.
54+
55+
### Composer
56+
3057
You can install PHP-Scoper with Composer:
3158

3259
```bash
33-
composer global require humbug/php-scoper:dev-master
60+
composer global require humbug/php-scoper
3461
```
3562

3663
If you cannot install it because of a dependency conflict or you prefer to
@@ -39,41 +66,211 @@ install it for your project, we recommend you to take a look at
3966

4067
```bash
4168
composer require --dev bamarni/composer-bin-plugin
42-
composer bin php-scoper require --dev humbug/php-scoper:dev-master
69+
composer bin php-scoper require --dev humbug/php-scoper
4370
```
4471

45-
A PHAR should be availaible soon as well.
46-
47-
4872
## Usage
4973

5074
```bash
5175
php-scoper add-prefix
5276
```
5377

54-
This will prefix all the files found in the current working directory.
55-
The prefixed files will be accessible in a `build` folder. You can
78+
This will prefix all relevant namespaces in code found in the current working
79+
directory. The prefixed files will be accessible in a `build` folder. You can
5680
then use the prefixed code to build your PHAR.
5781

58-
**Warning**: After prefexing the files, if you are relying on Composer
82+
**Warning**: After prefixing the files, if you are relying on Composer
5983
for the autoloading, dumping the autoloader again is required.
6084

6185
For a more concrete example, you can take a look at PHP-Scoper's build
62-
step in [Makefile](Makefile).
86+
step in [Makefile](Makefile), especially if you are using Composer as
87+
there are steps both before and after running PHP-Scoper to consider.
88+
89+
Refer to TBD for an in-depth look at scoping and building a PHAR taken from
90+
PHP-Scoper's makefile.
91+
92+
## Patchers
93+
94+
When scoping PHP files, there will be scenarios where some of the code being
95+
scoped indirectly references the original namespace. These will include, for
96+
example, strings or string manipulations. PHP-Scoper has limited support for
97+
prefixing such strings, so you may need to define `patchers`, one or more
98+
callables in a `scoper.inc.php` configuration file which can be used to replace
99+
some of the code being scoped.
100+
101+
Here's a simple example:
102+
103+
* Class names in strings.
104+
105+
You can imagine instantiating a class from a variable which is based on a
106+
known namespace, but also on a variable classname which is selected at
107+
runtime. Perhaps code similar to:
108+
109+
```php
110+
$type = 'Foo'; // determined at runtime
111+
$class = 'Humbug\\Format\\Type\\' . $type;
112+
```
113+
114+
If we scoped the `Humbug` namespace to `PhpScoperABC\Humbug`, then the above
115+
snippet would fail as PHP-Scoper cannot interpret the above as being a namespaced
116+
class. To complete the scoping successfully, a) the problem must
117+
be located and b) the offending line replaced.
118+
119+
The patched code which would resolve this issue might be:
120+
121+
```php
122+
$type = 'Foo'; // determined at runtime
123+
$scopedPrefix = array_shift(explode('\\', __NAMESPACE__));
124+
$class = $scopedPrefix . '\\Humbug\\Format\\Type\\' . $type;
125+
```
126+
127+
This and similar issues *may* arise after scoping, and can be debugged by
128+
running the scoped code and checking for issues. For this purpose, having a
129+
couple of end to end tests to validate post-scoped code or PHARs is recommended.
130+
131+
Applying such a change can be achieved by defining a suitable patcher in
132+
`scoper.inc.php`:
133+
134+
```php
135+
<?php declare(strict_types=1)
136+
137+
// scoper.inc.php
138+
139+
return [
140+
'patchers' => [
141+
function (string $filePath, string $prefix, string $content): string {
142+
//
143+
// PHP-Parser patch conditions for file targets
144+
//
145+
if ($filePath === '/path/to/offending/file') {
146+
return preg_replace(
147+
"%\$class = 'Humbug\\\\Format\\\\Type\\\\' . \$type;%",
148+
'$class = \'' . $prefix . '\\\\Humbug\\\\Format\\\\Type\\\\\' . $type;',
149+
$content
150+
);
151+
}
152+
return $content;
153+
},
154+
],
155+
];
156+
```
157+
158+
## Global Namespace Whitelisting
159+
160+
By default, PHP-Scoper only scopes (or prefixes) code where the namespace is
161+
non-global. In other words, non-namespaced code is not scoped. This leaves the
162+
majority of classes, functions and constants in PHP, and most extensions,
163+
untouched.
164+
165+
This is not necessarily a desireable outcome for vendor dependencies which are
166+
also not namespaced. To ensure they are isolated, you can configure PHP-Scoper to
167+
allow their prefixing from `scoper.inc.php` using basic strings or callables:
168+
169+
```php
170+
<?php declare(strict_types=1)
63171

172+
// scoper.inc.php
173+
174+
return [
175+
'global_namespace_whitelist' => [
176+
'AppKernel',
177+
function ($className) {
178+
return 'PHPUnit' === substr($className, 0, 6);
179+
},
180+
],
181+
'patchers' => [
182+
// patchers if relevant
183+
],
184+
];
185+
```
186+
187+
In this example, we're ensuring that the `AppKernal` class, and any
188+
non-namespaced PHPUnit packages are prefixed.
64189

65190
## Contributing
66191

67192
[Contribution Guide](CONTRIBUTING.md)
68193

194+
## Building A Scoped PHAR
195+
196+
This is a brief run through of the basic steps encoded in PHP-Scoper's own
197+
[Makefile](Makefile) and elsewhere to build a PHAR from scoped code.
198+
199+
### Step 1: Configure build location and prep vendors
200+
201+
If, for example, you are using [Box](box) to build your PHAR, you
202+
should set the `base-path` configuration option in your `box.json` file
203+
to point at the directory which will host scoped code. PHP-Scoper,
204+
by default, creates a `build` directory relative to the current working
205+
directory.
206+
207+
```js
208+
"base-path": "build"
209+
```
210+
211+
Assuming you need no dev dependencies, run:
212+
213+
```bash
214+
composer install --no-dev --prefer-dist
215+
```
216+
217+
### Step 2: Run PHP-Scoper
218+
219+
PHP-Scoper copies code to a new location during prefixing, leaving your original
220+
code untouched. The default location is `./build`. You can change the default
221+
location using the `--output-dir` option. By default, it also generates a random
222+
prefix string. You can set a specific prefix string using the `--prefix` option.
223+
If automating builds, you can set the `--force` option to overwrite any code
224+
existing in the output directory without being asked to confirm.
225+
226+
Onto the basic command assuming default options from your project's root
227+
directory:
228+
229+
```bash
230+
bin/php-scoper add-prefix
231+
```
232+
233+
As there are no path arguments, the current working directory will be scoped to
234+
`./build` in its entirety. Of course, actual prefixing is limited to PHP files,
235+
or PHP scripts. Other files are copied unchanged, though we also need to scope
236+
certain Composer related files.
237+
238+
Speaking of scoping Composer related files... The next step is to dump the
239+
Composer autoloader if we depend on it, so everything works as expected:
240+
241+
```bash
242+
composer dump-autoload -d build --classmap-authoritative
243+
```
244+
245+
### Step 3: Build, test, and cleanup
246+
247+
If using [Box](box), you can now move onto actually building the PHAR:
248+
249+
```bash
250+
php -d phar.readonly=0 box build -vvv
251+
```
252+
253+
At this point, it's best to have some simple end-to-end tests automated to put
254+
the PHAR through its paces and locate any problems (see Patchers and Whitelists
255+
from earlier in this README). Assuming it passes testing, the PHAR is ready.
256+
257+
Cleanup is simply to optionally delete `./build` contents, and remember to
258+
re-install dev dependencies removed during Step 1:
259+
260+
```bash
261+
composer install
262+
```
69263

70264
## Credits
71265

72-
Project originally created by: [Bernhard Schussek] ([@webmozart]) which has then been moved under the
266+
Project originally created by: [Bernhard Schussek] ([@webmozart]) which has
267+
now been moved under the
73268
[Humbug umbrella][humbug].
74269

75270

76271
[Bernhard Schussek]: https://webmozart.io/
77272
[@webmozart]: https://twitter.com/webmozart
78273
[humbug]: https://github.com/humbug
79-
[bamarni/composer-bin-plugin]: https://github.com/bamarni/composer-bin-plugin
274+
[bamarni/composer-bin-plugin]: https://github.com/bamarni/composer-bin-plugin
275+
[box]: https://github.com/box-project/box2
276+
[releases]: https://github.com/humbug/php-scoper/releases

0 commit comments

Comments
 (0)