PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.3
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.3
16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / vendor / wp-php-toolkit / data-liberation / README.md
jetpack / vendor / wp-php-toolkit / data-liberation Last commit date
BlockMarkup 6 days ago CSS 6 days ago DataFormatConsumer 6 days ago DataFormatProducer 6 days ago EntityReader 6 days ago EntityWriter 6 days ago Importer 6 days ago URL 6 days ago vendor-patched 6 days ago LICENSE.md 6 days ago README.md 6 days ago class-dataliberationexception.php 6 days ago class-dataliberationhtmlprocessor.php 6 days ago class-importentity.php 6 days ago composer.json 6 days ago rector.php 6 days ago
README.md
284 lines
1 ---
2 slug: dataliberation
3 title: DataLiberation
4 install: wp-php-toolkit/data-liberation
5
6 see_also:
7 - ../learn/03-importing-content.html | Tutorial — Markdown to WXR | The chapter that walks through importing a folder of Markdown files into WordPress via the toolkit.
8 - markdown | Markdown | Use Markdown as a source or destination format.
9 - blockparser | BlockParser | Analyze serialized blocks inside post content.
10 - httpclient | HttpClient | Download media and remote source data while importing.
11 ---
12
13 Streaming WordPress import/export. WXR, SQL, block markup — process entities one at a time instead of building whole-dataset object graphs.
14
15 ## Why this exists
16
17 <p>WordPress content should be portable, but real migrations cross several formats. A site export might arrive as WXR, a Markdown folder, or entities from another CMS. URLs can hide in block attributes, HTML, CSS, feeds, GUIDs, and post meta. Importers must also resume after a failed media download or upload.</p>
18
19 <p>The DataLiberation component streams WordPress-shaped data through readers, transformers, and writers. It models posts, terms, comments, attachments, and metadata as <code>ImportEntity</code> objects, then lets a pipeline rewrite each entity without loading the full export into memory.</p>
20
21 <p>The API reflects specific migration bugs: relative URLs in known block attributes, URLs inside inline CSS, self-closing block comments that must keep their shape, and origin-only URLs whose trailing slash style should not change during a rewrite.</p>
22
23 <p>Reach for it when the job combines formats: build WXR from another CMS, rewrite a staging export for production, frontload remote assets, or compose Markdown, XML, HTML, CSS, and URL rewriting into one pipeline.</p>
24
25 ## Write a WXR file in five lines
26
27 <p>Stream a single post into a WXR document via <code>WXRWriter</code>. The writer emits each entity to the output stream and only keeps the small amount of state needed for the current document.</p>
28
29 <!-- snippet:
30 filename: wxr-quickstart.php
31 runnable: true
32 -->
33 ```php
34 <?php
35 require '/php-toolkit/vendor/autoload.php';
36
37 use WordPress\ByteStream\MemoryPipe;
38 use WordPress\DataLiberation\EntityWriter\WXRWriter;
39 use WordPress\DataLiberation\ImportEntity;
40
41 $pipe = new MemoryPipe();
42 $writer = new WXRWriter( $pipe );
43 $writer->append_entity( new ImportEntity( 'post', array(
44 'post_title' => 'Hello',
45 'content' => 'World.',
46 'post_id' => '1',
47 'status' => 'publish',
48 ) ) );
49 $writer->finalize();
50 $writer->close_writing();
51 $pipe->close_writing();
52 $wxr = $pipe->consume_all();
53
54 echo "bytes: " . strlen( $wxr ) . "\n";
55 echo false !== strpos( $wxr, '<title>Hello</title>' ) ? "title exported\n" : "title missing\n";
56 echo false !== strpos( $wxr, '<wp:status>publish</wp:status>' ) ? "status exported\n" : "status missing\n";
57 ```
58
59 <!-- expected-output -->
60 ```
61 bytes: 475
62 title exported
63 status exported
64 ```
65
66 ## Build a WXR programmatically from any source
67
68 <p>The writer doesn't care where entities come from. Loop over rows from a CMS, a CSV, or a Notion API dump and emit posts plus their meta and comments.</p>
69
70 <!-- snippet:
71 filename: build-wxr.php
72 runnable: true
73 -->
74 ```php
75 <?php
76 require '/php-toolkit/vendor/autoload.php';
77
78 use WordPress\ByteStream\MemoryPipe;
79 use WordPress\DataLiberation\EntityWriter\WXRWriter;
80 use WordPress\DataLiberation\ImportEntity;
81
82 $rows = array(
83 array( 'id' => 10, 'title' => 'About', 'body' => '<p>About us.</p>', 'tags' => array( 'company' ) ),
84 array( 'id' => 11, 'title' => 'Blog', 'body' => '<p>Hello world.</p>', 'tags' => array( 'news', 'launch' ) ),
85 );
86
87 $pipe = new MemoryPipe();
88 $writer = new WXRWriter( $pipe );
89
90 foreach ( $rows as $row ) {
91 $writer->append_entity( new ImportEntity( 'post', array(
92 'post_id' => (string) $row['id'],
93 'post_title' => $row['title'],
94 'content' => $row['body'],
95 'status' => 'publish',
96 'post_type' => 'post',
97 ) ) );
98 foreach ( $row['tags'] as $i => $tag ) {
99 $writer->append_entity( new ImportEntity( 'term', array(
100 'term_id' => (string) ( $row['id'] * 100 + $i ),
101 'taxonomy' => 'post_tag',
102 'slug' => $tag,
103 'parent' => '0',
104 ) ) );
105 }
106 }
107
108 $writer->finalize();
109 $writer->close_writing();
110 $pipe->close_writing();
111
112 $wxr = $pipe->consume_all();
113 echo "items: " . substr_count( $wxr, '<item>' ) . "\n";
114 echo "terms: " . substr_count( $wxr, '<wp:term>' ) . "\n";
115 echo false !== strpos( $wxr, '<title>Blog</title>' ) ? "Blog post exported\n" : "Blog post missing\n";
116 ```
117
118 <!-- expected-output -->
119 ```
120 items: 2
121 terms: 3
122 Blog post exported
123 ```
124
125 ## Read entities from a WXR file incrementally
126
127 <p><code>WXREntityReader</code> emits one entity at a time. Memory use is driven by the current entity and parser buffers rather than the total file size.</p>
128
129 <!-- snippet:
130 filename: wxr-read.php
131 runnable: true
132 -->
133 ```php
134 <?php
135 require '/php-toolkit/vendor/autoload.php';
136
137 use WordPress\DataLiberation\EntityReader\WXREntityReader;
138
139 $wxr = <<<XML
140 <?xml version="1.0" encoding="UTF-8" ?>
141 <rss version="2.0" xmlns:wp="http://wordpress.org/export/1.2/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
142 <channel>
143 <title>Demo</title>
144 <item><title>First</title><wp:post_id>1</wp:post_id><wp:post_type>post</wp:post_type><content:encoded>Body 1</content:encoded></item>
145 <item><title>Second</title><wp:post_id>2</wp:post_id><wp:post_type>post</wp:post_type><content:encoded>Body 2</content:encoded></item>
146 </channel>
147 </rss>
148 XML;
149
150 $reader = WXREntityReader::create();
151 $reader->append_bytes( $wxr );
152 $reader->input_finished();
153
154 while ( $reader->next_entity() ) {
155 $entity = $reader->get_entity();
156 echo $entity->get_type() . ': ' . json_encode( $entity->get_data() ) . "\n";
157 }
158 ```
159
160 <!-- expected-output -->
161 ```
162 site_option: {"option_name":"blogname","option_value":"Demo"}
163 post: {"post_title":"First","post_id":"1","post_type":"post","post_content":"Body 1"}
164 post: {"post_title":"Second","post_id":"2","post_type":"post","post_content":"Body 2"}
165 ```
166
167 ## Streaming transform: rewrite URLs while copying WXR
168
169 <p>Wire reader to writer to rewrite a WXR file on the fly. This pattern is how you migrate a staging export to production: swap <code>staging.example.com</code> for <code>example.com</code> while holding only the current entity and output buffers.</p>
170
171 <!-- snippet:
172 filename: rewrite-urls.php
173 runnable: true
174 -->
175 ```php
176 <?php
177 require '/php-toolkit/vendor/autoload.php';
178
179 use WordPress\ByteStream\MemoryPipe;
180 use WordPress\DataLiberation\EntityReader\WXREntityReader;
181 use WordPress\DataLiberation\EntityWriter\WXRWriter;
182 use WordPress\DataLiberation\ImportEntity;
183
184 $source_xml = <<<XML
185 <?xml version="1.0" encoding="UTF-8" ?>
186 <rss version="2.0" xmlns:wp="http://wordpress.org/export/1.2/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
187 <channel>
188 <item><title>Hello</title><wp:post_id>1</wp:post_id><wp:post_type>post</wp:post_type>
189 <content:encoded>Visit https://staging.example.com/about for more.</content:encoded></item>
190 </channel>
191 </rss>
192 XML;
193
194 $reader = WXREntityReader::create();
195 $reader->append_bytes( $source_xml );
196 $reader->input_finished();
197
198 $out_pipe = new MemoryPipe();
199 $writer = new WXRWriter( $out_pipe );
200
201 while ( $reader->next_entity() ) {
202 $entity = $reader->get_entity();
203 $data = $entity->get_data();
204 foreach ( array( 'post_content', 'content', 'description' ) as $field ) {
205 if ( isset( $data[ $field ] ) ) {
206 $data[ $field ] = str_replace( 'staging.example.com', 'example.com', $data[ $field ] );
207 }
208 }
209 if ( 'post' === $entity->get_type() ) {
210 $data['content'] = isset( $data['post_content'] ) ? $data['post_content'] : ( isset( $data['content'] ) ? $data['content'] : '' );
211 }
212 $writer->append_entity( new ImportEntity( $entity->get_type(), $data ) );
213 }
214
215 $writer->finalize();
216 $writer->close_writing();
217 $out_pipe->close_writing();
218
219 $wxr = $out_pipe->consume_all();
220 echo false !== strpos( $wxr, 'https://example.com/about' ) ? "new URL present\n" : "new URL missing\n";
221 echo false === strpos( $wxr, 'staging.example.com' ) ? "old URL removed\n" : "old URL still present\n";
222 ```
223
224 <!-- expected-output -->
225 ```
226 new URL present
227 old URL removed
228 ```
229
230 ## Render Markdown into a WXR import in one pipeline
231
232 <p>Compose <code>MarkdownConsumer</code> with <code>WXRWriter</code> to publish a folder of Markdown directly as a WordPress import file.</p>
233
234 <!-- snippet:
235 filename: md-to-wxr.php
236 runnable: true
237 -->
238 ```php
239 <?php
240 require '/php-toolkit/vendor/autoload.php';
241
242 use WordPress\ByteStream\MemoryPipe;
243 use WordPress\DataLiberation\EntityWriter\WXRWriter;
244 use WordPress\DataLiberation\ImportEntity;
245 use WordPress\Markdown\MarkdownConsumer;
246
247 @mkdir( '/tmp/md-src', 0777, true );
248 file_put_contents( '/tmp/md-src/hello.md', "---\ntitle: Hello\n---\n\n# Hello\n\nFirst post." );
249 file_put_contents( '/tmp/md-src/second.md', "---\ntitle: Second\n---\n\nMore text **here**." );
250
251 $pipe = new MemoryPipe();
252 $writer = new WXRWriter( $pipe );
253
254 $id = 1;
255 foreach ( glob( '/tmp/md-src/*.md' ) as $path ) {
256 $consumer = new MarkdownConsumer( file_get_contents( $path ) );
257 $consumer->consume();
258 $writer->append_entity( new ImportEntity( 'post', array(
259 'post_id' => (string) $id++,
260 'post_title' => $consumer->get_meta_value( 'title' ) ?: basename( $path, '.md' ),
261 'content' => $consumer->get_block_markup(),
262 'status' => 'publish',
263 'post_type' => 'post',
264 'post_name' => basename( $path, '.md' ),
265 ) ) );
266 }
267
268 $writer->finalize();
269 $writer->close_writing();
270 $pipe->close_writing();
271
272 $wxr = $pipe->consume_all();
273 echo "posts: " . substr_count( $wxr, '<item>' ) . "\n";
274 echo false !== strpos( $wxr, '&lt;!-- wp:heading' ) ? "block markup exported\n" : "block markup missing\n";
275 echo false !== strpos( $wxr, '<title>Second</title>' ) ? "frontmatter title exported\n" : "frontmatter title missing\n";
276 ```
277
278 <!-- expected-output -->
279 ```
280 posts: 2
281 block markup exported
282 frontmatter title exported
283 ```
284