PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.0
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.0
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / form-builder / class-field-registry.php

class-field-registry.php in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 4.0.0, at form-builder/class-field-registry.php

147 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Registry of decentralized Gutenberg blocks for the Form Builder.
4 *
5 * Reads block.json files from the blocks directory to build the registry.
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) exit;
9
10 class WPPB_FB_Field_Registry {
11 private static $blocks = null;
12
13 // Reverse index (block name → field type), built lazily so `by_block_name()`
14 // is O(1) — the flatten loop calls it once per PB block on canvas.
15 private static $by_block_name = null;
16
17 // Per-request parse cache, keyed by absolute path. Both `all()` and
18 // `wppb_fb_register_blocks()` scan the same directory, so routing both through
19 // here decodes each block.json at most once per request.
20 private static $json_cache = array();
21
22 /**
23 * Read/decode block.json once per request. Callers get a copy.
24 *
25 * @param string $path Absolute path to a block.json.
26 * @return array|null
27 */
28 public static function read_block_json( $path ) {
29 if ( array_key_exists( $path, self::$json_cache ) ) {
30 return self::$json_cache[ $path ];
31 }
32 $data = null;
33 if ( is_readable( $path ) ) {
34 $decoded = json_decode( file_get_contents( $path ), true );
35 if ( is_array( $decoded ) ) {
36 $data = $decoded;
37 }
38 }
39 self::$json_cache[ $path ] = $data;
40 return $data;
41 }
42
43 public static function all() {
44 if ( self::$blocks !== null ) return self::$blocks;
45
46 self::$blocks = array();
47 $blocks_dir = __DIR__ . '/blocks/src/blocks';
48 if ( ! is_dir( $blocks_dir ) ) return array();
49
50 $cf_disabled = wppb_fb_cf_disabled_field_types();
51 $no_meta_name_types = wppb_fb_no_meta_name_field_types();
52 $default_meta_map = wppb_fb_default_field_meta_names();
53
54 $it = new DirectoryIterator( $blocks_dir );
55 foreach ( $it as $fileinfo ) {
56 if ( $fileinfo->isDir() && ! $fileinfo->isDot() ) {
57 $json_file = $fileinfo->getPathname() . '/block.json';
58 $json = self::read_block_json( $json_file );
59 if ( $json && isset( $json['name'] ) ) {
60 // The field registry is strictly for `profile-builder/field-*`
61 // blocks. Structural blocks (e.g. profile-builder/msf-step-break)
62 // share the directory tree for bundling convenience but must
63 // not appear here — they don't round-trip through manage_fields.
64 if ( strpos( $json['name'], 'profile-builder/field-' ) !== 0 ) continue;
65
66 // block.json's `wppbFieldType` is the canonical field-type
67 // label; see get_field_type_from_block_name().
68 $field_type = self::get_field_type_from_block_name( $json['name'], $json );
69 $attributes = isset( $json['attributes'] ) ? $json['attributes'] : array();
70
71 // Any cross-cutting attribute MUST be injected through this
72 // shared helper, or this shadow (which the flatten projection
73 // reads) and the runtime registration silently drift.
74 $attributes = wppb_fb_compute_field_attributes(
75 $attributes,
76 $field_type,
77 $json['name'],
78 array(
79 'cf_disabled' => $cf_disabled,
80 'default_meta_map' => $default_meta_map,
81 'no_meta_name_types' => $no_meta_name_types,
82 'context_label' => 'registry',
83 )
84 );
85
86 self::$blocks[ $field_type ] = array(
87 'block' => $json['name'],
88 'title' => isset( $json['title'] ) ? $json['title'] : $field_type,
89 'icon' => isset( $json['icon'] ) ? $json['icon'] : 'admin-generic',
90 'attributes' => $attributes,
91 // Add-on-provided type: stays in the registry so existing
92 // instances render, but the inserter gates it. See
93 // wppb_fb_inactive_addon_field_blocks().
94 'addon_gated' => ! empty( $json['wppbAddonFieldType'] ),
95 );
96 }
97 }
98 }
99
100 return self::$blocks;
101 }
102
103 /**
104 * PB field-type label from block name. Prefer `$metadata['wppbFieldType']` when present.
105 *
106 * @param string $block_name e.g. "profile-builder/field-input"
107 * @param array|null $metadata Optional parsed block.json.
108 * @return string
109 */
110 public static function get_field_type_from_block_name( $block_name, $metadata = null ) {
111 if ( is_array( $metadata ) && ! empty( $metadata['wppbFieldType'] ) ) {
112 return (string) $metadata['wppbFieldType'];
113 }
114 // Slug fallback, title-casing EVERY word so `field-foo-bar` becomes
115 // `Foo Bar`. Surfaced under WP_DEBUG so silent drift gets caught.
116 $slug = str_replace( 'profile-builder/field-', '', $block_name );
117 if ( defined( 'WP_DEBUG' ) && WP_DEBUG && function_exists( '_doing_it_wrong' ) ) {
118 _doing_it_wrong(
119 __METHOD__,
120 sprintf(
121 'Falling back to slug derivation for block name %s — pass the parsed block.json as $metadata so wppbFieldType is honored.',
122 esc_html( $block_name )
123 ),
124 '3.16.0'
125 );
126 }
127 return ucwords( str_replace( '-', ' ', $slug ) );
128 }
129
130 public static function by_block_name( $block_name ) {
131 if ( self::$by_block_name === null ) {
132 $blocks = self::all();
133 self::$by_block_name = array();
134 foreach ( $blocks as $type => $data ) {
135 if ( isset( $data['block'] ) ) {
136 self::$by_block_name[ $data['block'] ] = $type;
137 }
138 }
139 }
140 if ( ! isset( self::$by_block_name[ $block_name ] ) ) {
141 return null;
142 }
143 $type = self::$by_block_name[ $block_name ];
144 return array_merge( self::$blocks[ $type ], array( 'field_type' => $type ) );
145 }
146 }
147