@@ -78,12 +78,25 @@ public function parse( string $blueprint_string ): Blueprint {
7878 // Create the execution plan.
7979 $ execution_plan = $ this ->createExecutionPlan ( $ blueprint_array );
8080
81+ // Collect errors from the plan.
82+ foreach ( $ execution_plan as $ step ) {
83+ if ( isset ( $ step ['errors ' ] ) && count ( $ step ['errors ' ] ) > 0 ) {
84+ $ errors [$ step ['key ' ]] = array_merge (
85+ $ errors [$ step ['key ' ]] ?? [],
86+ $ step ['errors ' ]
87+ );
88+ } else {
89+ unset( $ step ['errors ' ] );
90+ }
91+ }
92+
8193 return new Blueprint (
8294 $ blueprint_string ,
8395 $ blueprint_array ,
8496 $ php_version_constraint ,
8597 $ wp_version_constraint ,
86- $ execution_plan
98+ $ execution_plan ,
99+ $ errors
87100 );
88101 }
89102
@@ -313,17 +326,21 @@ private function createExecutionPlan( array $blueprint ): array {
313326
314327 private function buildConstantsStep ( array $ constants ): array {
315328 return [
329+ 'key ' => 'constants ' ,
316330 'name ' => 'defineConstants ' ,
317331 'args ' => [ 'constants ' => $ constants ],
332+ 'errors ' => [],
318333 ];
319334 }
320335
321336 private function buildSiteOptionsStep ( array $ site_options ): array {
322337 // Ensure siteUrl is not included as per schema Omit<>
323338 unset( $ site_options ['siteUrl ' ] );
324339 return [
340+ 'key ' => 'siteOptions ' ,
325341 'name ' => 'setSiteOptions ' ,
326342 'args ' => [ 'options ' => $ site_options ],
343+ 'errors ' => [],
327344 ];
328345 }
329346
@@ -339,14 +356,17 @@ private function buildMuPluginsStep( array $mu_plugins ): array {
339356 }
340357
341358 return [
359+ 'key ' => 'muPlugins ' ,
342360 'name ' => 'writeFiles ' ,
343361 'args ' => [ 'files ' => $ files ],
362+ 'errors ' => [],
344363 ];
345364 }
346365
347366 private function buildThemeStep ( $ theme ): array {
348367 if ( is_string ( $ theme ) ) {
349368 $ step = [
369+ 'key ' => 'themes ' ,
350370 'name ' => 'installTheme ' ,
351371 'args ' => [
352372 'source ' => $ theme ,
@@ -357,6 +377,7 @@ private function buildThemeStep( $theme ): array {
357377 } elseif ( is_array ( $ theme ) && isset ( $ theme ['source ' ] ) && is_string ( $ theme ['source ' ] ) ) {
358378 // Pass through the raw definition for extensibility.
359379 $ step = [
380+ 'key ' => 'themes ' ,
360381 'name ' => 'installTheme ' ,
361382 'args ' => [
362383 'source ' => $ theme ['source ' ],
@@ -369,6 +390,8 @@ private function buildThemeStep( $theme ): array {
369390 throw new InvalidArgumentException ( 'Invalid theme reference format in "themes" array. ' );
370391 }
371392
393+ $ error = $ this ->validateDataSource ( $ step ['args ' ]['source ' ], 'wp-content/themes/* ' );
394+ $ step ['errors ' ] = $ error ? [$ error ] : [];
372395 return $ step ;
373396 }
374397
@@ -377,73 +400,123 @@ private function buildActiveThemeStep( $theme ): array {
377400 $ theme = [ 'source ' => $ theme ];
378401 }
379402 $ theme ['active ' ] = true ;
380- return $ this ->buildThemeStep ( $ theme );
403+ $ step = $ this ->buildThemeStep ( $ theme );
404+ $ step ['key ' ] = 'activeTheme ' ;
405+ return $ step ;
381406 }
382407
383408 private function buildPluginStep ( $ plugin ): array {
384409 if ( is_string ( $ plugin ) ) {
385410 $ plugin = [ 'source ' => $ plugin ];
386411 }
387412
413+ $ error = $ this ->validateDataSource ( $ plugin ['source ' ], 'wp-content/plugins/* ' );
388414 return [
415+ 'key ' => 'plugins ' ,
389416 'name ' => 'installPlugin ' ,
390417 'args ' => $ plugin ,
418+ 'errors ' => $ error ? [$ error ] : [],
391419 ];
392420 }
393421
394422 private function buildMediaStep ( $ media ): array {
423+ $ errors = [];
424+ foreach ( $ media as $ media_def ) {
425+ $ source = is_array ( $ media_def ) ? $ media_def ['source ' ] : $ media_def ;
426+ $ error = $ this ->validateDataSource ( $ source , 'wp-content/uploads/* ' );
427+ if ( $ error ) {
428+ $ errors [] = $ error ;
429+ }
430+ }
431+
395432 return [
433+ 'key ' => 'media ' ,
396434 'name ' => 'importMedia ' ,
397435 'args ' => [ 'media ' => $ media ],
436+ 'errors ' => $ errors ,
398437 ];
399438 }
400439
401440 private function buildSiteLanguageStep ( $ site_language ): array {
402441 return [
442+ 'key ' => 'siteLanguage ' ,
403443 'name ' => 'setSiteLanguage ' ,
404444 'args ' => [ 'language ' => $ site_language ],
445+ 'errors ' => [],
405446 ];
406447 }
407448
408449 private function buildRolesStep ( $ roles ): array {
409450 return [
451+ 'key ' => 'roles ' ,
410452 'name ' => 'createRoles ' ,
411453 'args ' => [ 'roles ' => $ roles ],
454+ 'errors ' => [],
412455 ];
413456 }
414457
415458 private function buildUsersStep ( $ users ): array {
416459 return [
460+ 'key ' => 'users ' ,
417461 'name ' => 'createUsers ' ,
418462 'args ' => [ 'users ' => $ users ],
463+ 'errors ' => [],
419464 ];
420465 }
421466
422467 private function buildPostTypesStep ( $ post_types ): array {
423468 return [
469+ 'key ' => 'postTypes ' ,
424470 'name ' => 'createPostTypes ' ,
425471 'args ' => [ 'postTypes ' => $ post_types ],
472+ 'errors ' => [],
426473 ];
427474 }
428475
429476 private function buildContentStep ( $ content ): array {
430477 // @TODO: Consider splitting this into multiple importContent steps,
431478 // one per piece of content.
479+ $ errors = [];
480+ foreach ( $ content as $ content_def ) {
481+ if ( 'posts ' === $ content_def ['type ' ] ) {
482+ if ( isset ( $ content_def ['source ' ] ) ) {
483+ $ sources = $ content_def ['source ' ];
484+ } else {
485+ $ sources = $ content_def ;
486+ }
487+
488+ if ( ! is_array ( $ sources ) ) {
489+ $ sources = [ $ sources ];
490+ }
491+
492+ foreach ( $ sources as $ source ) {
493+ $ error = $ this ->validateDataSource ( $ source , 'wp-content/content/posts/* ' );
494+ if ( $ error ) {
495+ $ errors [] = $ error ;
496+ }
497+ }
498+ }
499+ }
500+
432501 return [
502+ 'key ' => 'content ' ,
433503 'name ' => 'importContent ' ,
434504 'args ' => [ 'content ' => $ content ],
505+ 'errors ' => $ errors ,
435506 ];
436507 }
437508
438509 private function buildAdditionalStepsAfterExecution ( $ step_data ): array {
439510 return [
511+ 'key ' => 'additionalStepsAfterExecution ' ,
440512 'name ' => $ step_data ['step ' ],
441513 'args ' => $ step_data ,
514+ 'errors ' => [],
442515 ];
443516 }
444517
445518 private function validateDataSource ( string $ source , string $ allowed_pattern ): ?string {
446- if ( strlen ( $ source ) === 0 ) {
519+ if ( 0 === strlen ( $ source ) ) {
447520 return 'Source must be a non-empty string. ' ;
448521 }
449522
@@ -456,9 +529,9 @@ private function validateDataSource( string $source, string $allowed_pattern ):
456529 $ byte_1 = $ source [0 ];
457530 $ byte_2 = $ source [1 ] ?? null ;
458531 if ( str_contains ( $ source , '/ ' ) ) {
459- if ( $ byte_1 === ' / ' ) {
532+ if ( ' / ' === $ byte_1 ) {
460533 $ source = substr ( $ source , 1 );
461- } elseif ( $ byte_1 === ' . ' && $ byte_2 === ' / ' ) {
534+ } elseif ( ' . ' === $ byte_1 && ' / ' === $ byte_2 ) {
462535 $ source = substr ( $ source , 2 );
463536 }
464537
0 commit comments