Skip to content

Commit e023109

Browse files
committed
feat(main): release
0 parents  commit e023109

File tree

107 files changed

+23884
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+23884
-0
lines changed

.gitattributes

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
* text=auto eol=lf
2+
3+
*.png binary
4+
*.jpg binary
5+
*.jpeg binary
6+
*.gif binary
7+
8+
/tests/ export-ignore
9+
/.github/ export-ignore
10+
/.husky/ export-ignore
11+
12+
/bin/ export-ignore
13+
/playwright-report/ export-ignore
14+
/node_modules/ export-ignore
15+
test-results export-ignore
16+
17+
/.phpcs.xml.dist export-ignore
18+
.phpunit.result.cache export-ignore
19+
/.wp-env.json export-ignore
20+
/commitlint.config.js export-ignore
21+
/phpstan.neon.dist export-ignore
22+
/phpunit.xml.dist export-ignore
23+
/playwright.config.js export-ignore
24+
/.gitignore export-ignore
25+
/package.json export-ignore
26+
/package-lock.json export-ignore
27+
28+
/CODE_OF_CONDUCT.md export-ignore
29+
/CONTRIBUTING.md export-ignore
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Advanced Usage of WP File System
2+
3+
This documentation is intended for developers who want to gain a deeper understanding of how the WP File System library
4+
works and use its advanced features such as hooks, protected mode, and manual service instance creation.
5+
6+
## WordPress File System Basics and Access Methods
7+
8+
To understand the full power of the WPFS library, it's important to know what problem it solves at the lowest level.
9+
WordPress uses an abstraction for file access — WP_Filesystem — to ensure security and compatibility with various
10+
hosting environments. This abstraction can work in several modes (methods).
11+
12+
| Method | Description | When to Use |
13+
|:-------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------|
14+
| `direct` | The simplest and fastest method. File operations are performed directly using PHP functions (fopen, fwrite, etc.) under the web server user account (e.g., www-data). | Used by default on properly configured servers where the web server has write permissions to the required WordPress directories. |
15+
| `ssh2` | Operations are performed through a secure SSH connection to the server using provided credentials. | Used on servers where the web server doesn't have direct write permissions but has SSH access. Requires the PHP ssh2 extension. |
16+
| `ftpext` | Operations are performed via FTP using credentials of an FTP user who has write permissions. | Common method on virtual hosting. Requires the PHP ftp extension. |
17+
| `ftpsockets` | Alternative implementation of FTP client using pure PHP sockets. Used when the ftp extension is not available. | Fallback option for FTP that doesn't require additional PHP extensions. |
18+
19+
### Problems and Their Solution with WPFS
20+
21+
- Permission problem: If using `direct` method on a server where `www-data` doesn't have write permissions to the
22+
`wp-content/uploads` folder, any attempt to save a file will fail.
23+
- Credentials problem: Methods `ssh2` and `ftp*` require credentials. WordPress usually requests them from the user
24+
through a form in the admin panel. This makes it impossible to perform file operations in the background (e.g., via
25+
CRON) or through API.
26+
27+
### How WPFS Solves This:
28+
29+
1. Automatic initialization: WPFS automatically initializes WP_Filesystem, eliminating the need to do it manually.
30+
2. Using constants: WPFS relies on credentials being defined as constants in `wp-config.php` for non-interactive
31+
operations (CRON, WP-CLI). This is a standard WordPress practice.
32+
33+
### Configuration in wp-config.php
34+
35+
You can force the access method and specify the credentials in the `wp-config.php` file.
36+
37+
```php
38+
// Force filesystem method
39+
// Possible values: 'direct', 'ssh2', 'ftpext', 'ftpsockets'
40+
define('FS_METHOD', 'direct');
41+
42+
// MAIN CREDENTIALS (for FTP, FTPS, SSH2)
43+
define('FTP_HOST', 'your-server.com');
44+
define('FTP_USER', 'username');
45+
define('FTP_PASS', 'password');
46+
define('FTP_PORT', '22'); // Port 22 for SSH, 21 for FTP
47+
48+
// CONNECTION SETTINGS
49+
define('FTP_SSL', false); // true for FTPS (FTP methods only)
50+
51+
// SSH KEYS (alternative to password for SSH2)
52+
define('FTP_PUBKEY', '/home/user/.ssh/id_rsa.pub');
53+
define('FTP_PRIKEY', '/home/user/.ssh/id_rsa');
54+
55+
// DEFAULT ACCESS PERMISSIONS
56+
define('FS_CHMOD_DIR', 0755);
57+
define('FS_CHMOD_FILE', 0644);
58+
```
59+
60+
## Decorator Providers: Hooks and Guardian
61+
62+
By default, FSFactory creates basic service instances that simply call corresponding WordPress functions.
63+
However, you can globally enable two powerful decorators: FSHooksProvider and FSGuardiansProvider.
64+
65+
### FSGuardiansProvider (Guardian)
66+
67+
Purpose: Switches error handling mode. By default, according to WordPress API, most methods simply return `false` in
68+
case of failure. This can make debugging difficult. When "Guardian" is enabled, typed exceptions will be thrown instead
69+
of `false`.
70+
71+
- `FSPathNotFoundException`: Resource (file or directory) not found.
72+
- `FSPermissionException`: Resource exists, but insufficient permissions for the operation.
73+
- `FSException`: General filesystem error.
74+
75+
### Как использовать:
76+
77+
```php
78+
use Metapraxis\WPFileSystem\Provider\FSGuardiansProvider;
79+
use Metapraxis\WPFileSystem\Facade\WPFS;
80+
use Metapraxis\WPFileSystem\Exceptions\FSPathNotFoundException;
81+
82+
// Enabling exception mode
83+
FSGuardiansProvider::setStatus(true);
84+
85+
try {
86+
$content = WPFS::getContents('/path/to/non-existent-file.txt');
87+
} catch (FSPathNotFoundException $e) {
88+
wp_log_error($e->getMessage());
89+
} finally {
90+
// You can disable it, that is, use it as a temporary effect,
91+
// or not disable it and thereby introduce a global state.
92+
FSGuardiansProvider::setStatus(false);
93+
}
94+
```
95+
96+
### FSHooksProvider (Hooks)
97+
98+
Purpose: Adds the ability to "hook into" file operations using standard WordPress hooks
99+
(`do_action` and `apply_filters`). This makes it easy to add logging, monitoring, or modify operation behavior
100+
on the fly.
101+
102+
All hook names can be found in traits inside `src/Hooks/Collection/`.
103+
104+
### How to use:
105+
106+
```php
107+
use Metapraxis\WPFileSystem\Provider\FSHooksProvider;
108+
use Metapraxis\WPFileSystem\Facade\WPFS;
109+
use Metapraxis\WPFileSystem\Hooks\Collection\ActionHooks;
110+
111+
// Enabling hooks
112+
FSHooksProvider::setStatus(true);
113+
114+
// Adding an action that will be triggered AFTER writing to any file
115+
add_action(ActionHooks::$AFTER_PUT_CONTENTS_ACTION, function($result, $file, $contents) {
116+
if ($result) {
117+
// We record information about the successful recording in a separate log
118+
error_log(sprintf('File written successfully: %s, Size: %d bytes', $file, strlen($contents)));
119+
}
120+
}, 10, 3);
121+
122+
WPFS::putContents(WP_CONTENT_DIR . '/uploads/my-log.txt', 'New log entry.');
123+
124+
// Disabling hooks
125+
FSHooksProvider::setStatus(false);
126+
```
127+
128+
## Instance Caching in FSFactory
129+
130+
FSFactory caches created service instances to avoid unnecessary creation overhead. It's important to understand that the
131+
cache
132+
key depends not only on the class name but also on the current state of FSHooksProvider and FSGuardiansProvider.
133+
134+
This means that within a single request, there can be up to 4 different instances of the same service,
135+
for example, FSBaseReader:
136+
137+
1. Basic (hooks and protection disabled).
138+
2. With hooks only.
139+
3. With protection only.
140+
4. With both hooks and protection simultaneously.
141+
142+
The factory manages this cache itself, ensuring that you always get the correct instance according to the
143+
current provider configuration.
144+
145+
## Manual Instance Creation and Decoration
146+
147+
While the WPFS facade and FSFactory cover 99% of scenarios, you might want to manually create and configure
148+
a service instance. This can be useful for dependency injection in your classes.
149+
150+
The process follows the "Russian doll" principle (Decorator pattern):
151+
152+
1. Create a base adapter object.
153+
2. Wrap it in a hook decorator (if needed).
154+
3. Wrap the resulting object in a protection decorator (if needed).
155+
156+
```php
157+
global $wp_filesystem;
158+
159+
// 1. Creating a base instance
160+
$baseAction = new \Metapraxis\WPFileSystem\Adapters\FSBaseAction($wp_filesystem);
161+
162+
// 2. We wrap it in a hook decorator
163+
$hookableAction = new \Metapraxis\WPFileSystem\Hooks\HookableFSAction($baseAction);
164+
165+
// 3. We wrap the result in a protection decorator
166+
$guardedAction = new \Metapraxis\WPFileSystem\Guarded\GuardedFSBaseAction($hookableAction);
167+
168+
169+
// Now $guardedAction is a full-featured service that
170+
// will both perform hooks and throw exceptions in case of errors.
171+
// It can be passed to the constructor of another class.
172+
173+
class MyPluginService
174+
{
175+
private $filesystem;
176+
177+
public function __construct(\Metapraxis\WPFileSystem\Contracts\FSBaseAction $filesystem)
178+
{
179+
$this->filesystem = $filesystem;
180+
}
181+
182+
public function doSomething()
183+
{
184+
try {
185+
$this->filesystem->putContents('/path/to/file.txt', 'data');
186+
} catch (\Metapraxis\WPFileSystem\Exceptions\FSException $e) {
187+
// ...
188+
}
189+
}
190+
}
191+
192+
$service = new MyPluginService($guardedAction);
193+
$service->doSomething();
194+
```

0 commit comments

Comments
 (0)