From df16d57a807040dd0e340b8dabbc08cb443207ac Mon Sep 17 00:00:00 2001 From: Miika Arponen Date: Fri, 1 Dec 2017 11:18:26 +0200 Subject: [PATCH 1/9] More effective version of get_fields() included --- CHANGELOG.md | 7 +- plugin.php | 2 +- src/Codifier.php | 224 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9560465..327636c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.2.0-beta] - 2017-12-01 + +### Added +- Added a more effective version of ACF's get_fields() method to use with the Codifier + ## [1.1.3] - 2017-11-27 -## Changed +### Changed - Another small bug fix regarding Group field ## [1.1.2] - 2017-11-27 diff --git a/plugin.php b/plugin.php index ef1a5ff..7e88cee 100644 --- a/plugin.php +++ b/plugin.php @@ -3,7 +3,7 @@ Plugin Name: ACF Codifier Plugin URI: https://github.com/devgeniem/acf-codifier Description: A helper class to make defining ACF field groups and fields easier in the code. -Version: 1.1.3 +Version: 1.2.0-beta Author: Geniem Oy Author URI: https://geniem.fi License: GPLv2 diff --git a/src/Codifier.php b/src/Codifier.php index 97b45e5..c5db96d 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -100,4 +100,228 @@ public static function get_label_visibility( $field ) { return isset( self::$hidden_labels[ $key ] ); } + + /** + * A more efficient replacement for ACF' native get_fields function + * + * @param int $id Post id to fetch fields from + * @param array $wanted Keys for wanted fields. Empty or null default to all + * @return array Fields as an associative array + */ + public static function get_fields( $id, $wanted = null ) { + global $wpdb; + + // Get the raw meta data from the database + $rows = get_post_meta( $id ); + + // Filter only the keys we want + if ( ! empty( $wanted ) ) { + $rows = array_filter( $rows, function( $key ) { + // Strip possible trailing underscore + if ( substr( $key, 0, 1 ) === '_' ) { + $key = substr( $key, 1 ); + } + + return in_array( $key, $wanted ); + }, ARRAY_FILTER_USE_KEY ); + } + + // Sort the meta rows so that the ones deeper in the tree come last + if ( ! ( $sorted = wp_cache_get( 'sorted_meta_' . $id ) ) ) { + uksort( $rows, function ( $a, $b ) { + + preg_match_all( '/_(\d+)_/', $a, $a_amount ); + preg_match_all( '/_(\d+)_/', $b, $b_amount ); + $a_a = count( $a_amount[0] ); + $b_a = count( $b_amount[0] ); + + // If the depth is same, sort alphabetically + if ( $a_a === $b_a ) { + return $a <=> $b; + } else { + return $a_a <=> $b_a; + } + } ); + wp_cache_set( 'sorted_meta_' . $id, $rows ); + } + else { + $rows = $sorted; + } + + $original = []; + + // Convert to ordinary key-value form + foreach ( $rows as $key => $row ) { + $original[ $key ] = $row[0]; + } + + // Initiate a few arrays for the future + $times = []; + $fields = []; + $clones = []; + $layouts = []; + $layout_filters = []; + + // Loop through all meta values + foreach ( $original as $key => $value ) { + + // We don't want to handle meta-meta values for now + if ( $key[0] === '_' ) { + continue; + } + + // If a field doesn't have a meta-meta pair, we don't want to include it + if ( ! isset( $original[ '_' . $key ] ) ) { + continue; + } + else { + // Fetch the appropriate field object + $field_key = $original[ '_' . $key ]; + + if ( ! ( $field = wp_cache_get( $field_key ) ) ) { + $field = acf_get_local_field( $field_key ); + + // If there is a meta-meta pair but the field doesn't exist anymore, + // continue without returning the data either + if ( ! $field ) { + continue; + } + + // ACF needs this for some reason + $field['_name'] = $field['name']; + wp_cache_set( $field_key, $field ); + } + + // If there have been cloned fields, we need to run a few checks + if ( ! empty( $clones ) ) { + foreach ( $clones as $clone_key => $clone_value ) { + // Clone would be first in the actual key + $clone_key_array = explode( '_', $clone_key ); + $clone_key = $clone_key_array[0]; + + // Does the key start with a known clone key? + if ( substr( $key, 0, strlen( $clone_key .'_' ) ) === $clone_key .'_' ) { + $initial_path = [ $clone_key ]; + $path_key = substr( $key, strlen( $clone_key . '_' ) ); + } + else { + $path_key = $key; + $initial_path = []; + } + } + } + else { + $path_key = $key; + $initial_path = []; + } + + // Split the key for positioning the values in the tree + $path = preg_split( '/_(\d+)_/', $path_key, 0, PREG_SPLIT_DELIM_CAPTURE ); + + $path = array_merge( $initial_path, $path ); + + // Create a reference to the spot where the value is supposed to be placed + $value_node =& $fields; + + // Reference magic + foreach ( $path as $pkey ) { + $value_node =& $value_node[ $pkey ]; + } + + // Do field type specific things + switch( $field['type'] ) { + case 'clone': + // Get the cloned field's field object + if ( ! ( $field_object = wp_cache_get( 'local_field_' . $field['key'], 'acf' ) ) ) { + $field_object = acf_get_local_field( $field['key'] ); + wp_cache_set( 'local_field_' . $field['key'], $field_object, 'acf' ); + } + + // Loop through cloned fields and fetch their field objects + foreach ( $field_object['clone'] as $cloned_fields ) { + if ( ! ( $cloned_field = wp_cache_get( $cloned_fields, 'acf' ) ) ) { + $cloned_field = acf_get_local_field( $cloned_fields ); + wp_cache_set( $cloned_fields, $cloned_field, 'acf' ); + } + + // Store the objects for future use + $clones[ $key .'_' . $cloned_field['name'] ] = $cloned_field; + } + + // Create an empty node in the tree to store the actual values later + $value_node = []; + break; + case 'flexible_content': + // Flexible content field's value is an array of the layouts used + $layouts = maybe_unserialize( $value ); + + // Create an empty node in the tree to store the actual values + $value_node = []; + + // Loop through the layouts + foreach ( $layouts as $index => $layout ) { + // Insert the layout name in the data + $value_node[ $index ] = [ 'acf_fc_layout' => $layout ]; + if ( ! isset( $layout_filters[ $layout ] ) ) { + $layout_filters[ $layout ] = []; + } + // Store a reference to the layout node for DustPress Component filtering + $layout_filters[ $layout ][] =& $value_node[ $index ]; + } + break; + case 'repeater': + // Create an empty node in the tree to store the actual values later + $value_node = []; + break; + default: + if ( ! ( $value_node = wp_cache_get( $id .'-'. $key ) ) ) { + // Run the value through a bunch of filters to get the format we want + $value = maybe_unserialize( $value ); + $value = apply_filters( "acf/format_value", $value, $id, $field ); + $value = apply_filters( "acf/format_value/type={$field['type']}", $value, $id, $field ); + $value = apply_filters( "acf/format_value/name={$field['_name']}", $value, $id, $field ); + $value = apply_filters( "acf/format_value/key={$field['key']}", $value, $id, $field ); + $value_node = $value; + wp_cache_set( $id .'-'. $key, $value_node ); + } + break; + + } + + // Unset the reference to prevent odd bugs🐛 to appear + unset( $value_node ); + } + } + + // Sort the return array recursively by keys so that the fields are in the right order + // Should be a fairly quick operation because the array should be pretty much in order already + self::ksortRecursive( $fields ); + + // Run DustPress Components filters for the layouts through previously stored references + foreach( $layout_filters as $key => &$datas ) { + foreach( $datas as &$data ) { + $data = apply_filters( 'dustpress/components/data=' . $key, $data ); + } + } + + return $fields; + } + + /** + * A helper method to sort arrays recursively by key + * + * @param array $array An array to sort + * @param int $sort_flags Possible sorting flags + * @return void + */ + public static function ksortRecursive( &$array, $sort_flags = SORT_REGULAR ) { + if ( ! is_array( $array ) ) { + return false; + } + ksort( $array, $sort_flags ); + foreach ( $array as &$arr ) { + self::ksortRecursive( $arr, $sort_flags ); + } + return true; + } } From 4aea4112a1ab05a68d985e6323a31ceeba400db5 Mon Sep 17 00:00:00 2001 From: Miika Arponen Date: Fri, 1 Dec 2017 14:48:57 +0200 Subject: [PATCH 2/9] Small fixes --- src/Codifier.php | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Codifier.php b/src/Codifier.php index c5db96d..8d8a43b 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -127,19 +127,33 @@ public static function get_fields( $id, $wanted = null ) { } // Sort the meta rows so that the ones deeper in the tree come last - if ( ! ( $sorted = wp_cache_get( 'sorted_meta_' . $id ) ) ) { + $sorted = wp_cache_get( 'sorted_meta_' . $id ); + $sort_cache = []; + + if ( ! $sorted ) { uksort( $rows, function ( $a, $b ) { - - preg_match_all( '/_(\d+)_/', $a, $a_amount ); - preg_match_all( '/_(\d+)_/', $b, $b_amount ); - $a_a = count( $a_amount[0] ); - $b_a = count( $b_amount[0] ); + if ( isset( $sort_cache[ $a ] ) ) { + $a_amount = $sort_cache[ $a ]; + } + else { + preg_match_all( '/_(\d+)_/', $a, $a_amount ); + } + + if ( isset( $sort_cache[ $b ] ) ) { + $b_amount = $sort_cache[ $b ]; + } + else { + preg_match_all( '/_(\d+)_/', $b, $b_amount ); + } + + $a_amount = count( $a_amount[0] ); + $b_amount = count( $b_amount[0] ); // If the depth is same, sort alphabetically - if ( $a_a === $b_a ) { + if ( $a_amount === $b_amount ) { return $a <=> $b; } else { - return $a_a <=> $b_a; + return $a_amount <=> $b_amount; } } ); wp_cache_set( 'sorted_meta_' . $id, $rows ); @@ -178,7 +192,8 @@ public static function get_fields( $id, $wanted = null ) { // Fetch the appropriate field object $field_key = $original[ '_' . $key ]; - if ( ! ( $field = wp_cache_get( $field_key ) ) ) { + $field = wp_cache_get( $field_key ); + if ( ! $field ) { $field = acf_get_local_field( $field_key ); // If there is a meta-meta pair but the field doesn't exist anymore, @@ -232,14 +247,16 @@ public static function get_fields( $id, $wanted = null ) { switch( $field['type'] ) { case 'clone': // Get the cloned field's field object - if ( ! ( $field_object = wp_cache_get( 'local_field_' . $field['key'], 'acf' ) ) ) { + $field_object = wp_cache_get( 'local_field_' . $field['key'], 'acf' ); + if ( ! $field_object ) { $field_object = acf_get_local_field( $field['key'] ); wp_cache_set( 'local_field_' . $field['key'], $field_object, 'acf' ); } // Loop through cloned fields and fetch their field objects foreach ( $field_object['clone'] as $cloned_fields ) { - if ( ! ( $cloned_field = wp_cache_get( $cloned_fields, 'acf' ) ) ) { + $cloned_field = wp_cache_get( $cloned_fields, 'acf' ); + if ( ! $cloned_field ) { $cloned_field = acf_get_local_field( $cloned_fields ); wp_cache_set( $cloned_fields, $cloned_field, 'acf' ); } @@ -274,7 +291,9 @@ public static function get_fields( $id, $wanted = null ) { $value_node = []; break; default: - if ( ! ( $value_node = wp_cache_get( $id .'-'. $key ) ) ) { + $value_node = wp_cache_get( $id .'-'. $key ); + + if ( ! $value_node ) { // Run the value through a bunch of filters to get the format we want $value = maybe_unserialize( $value ); $value = apply_filters( "acf/format_value", $value, $id, $field ); From 95e8c67a8f96d21ae81df8703f5e9cb98e681443 Mon Sep 17 00:00:00 2001 From: Miika Arponen Date: Fri, 1 Dec 2017 14:53:21 +0200 Subject: [PATCH 3/9] Small fixes --- src/Codifier.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Codifier.php b/src/Codifier.php index 8d8a43b..fba82be 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -128,27 +128,31 @@ public static function get_fields( $id, $wanted = null ) { // Sort the meta rows so that the ones deeper in the tree come last $sorted = wp_cache_get( 'sorted_meta_' . $id ); - $sort_cache = []; if ( ! $sorted ) { + $sort_cache = []; + uksort( $rows, function ( $a, $b ) { + // Check if $a parse value is in cache if ( isset( $sort_cache[ $a ] ) ) { $a_amount = $sort_cache[ $a ]; } else { + // Find the depth from key preg_match_all( '/_(\d+)_/', $a, $a_amount ); + $a_amount = count( $a_amount[0] ); } + // Check if $b parse value is in cache if ( isset( $sort_cache[ $b ] ) ) { $b_amount = $sort_cache[ $b ]; } else { + // Find the depth from key preg_match_all( '/_(\d+)_/', $b, $b_amount ); + $b_amount = count( $b_amount[0] ); } - $a_amount = count( $a_amount[0] ); - $b_amount = count( $b_amount[0] ); - // If the depth is same, sort alphabetically if ( $a_amount === $b_amount ) { return $a <=> $b; @@ -292,7 +296,7 @@ public static function get_fields( $id, $wanted = null ) { break; default: $value_node = wp_cache_get( $id .'-'. $key ); - + if ( ! $value_node ) { // Run the value through a bunch of filters to get the format we want $value = maybe_unserialize( $value ); From 38cfd61322603a407510c57d8fa936932e7791e9 Mon Sep 17 00:00:00 2001 From: Miika Arponen Date: Fri, 1 Dec 2017 14:55:21 +0200 Subject: [PATCH 4/9] Small fixes --- src/Codifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codifier.php b/src/Codifier.php index fba82be..9bcf109 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -102,7 +102,7 @@ public static function get_label_visibility( $field ) { } /** - * A more efficient replacement for ACF' native get_fields function + * A more efficient replacement for ACF's native get_fields function * * @param int $id Post id to fetch fields from * @param array $wanted Keys for wanted fields. Empty or null default to all From b932bcf6b647a01ee052cf2128bd4c44cfa85c68 Mon Sep 17 00:00:00 2001 From: Miika Arponen Date: Fri, 1 Dec 2017 15:07:30 +0200 Subject: [PATCH 5/9] Enhanced comments --- src/Codifier.php | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Codifier.php b/src/Codifier.php index 9bcf109..68a8eb9 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -126,7 +126,8 @@ public static function get_fields( $id, $wanted = null ) { }, ARRAY_FILTER_USE_KEY ); } - // Sort the meta rows so that the ones deeper in the tree come last + // Sort the meta rows so that the ones deeper in the final data tree come last + // Return the sorted data from the cache if it's stored there $sorted = wp_cache_get( 'sorted_meta_' . $id ); if ( ! $sorted ) { @@ -160,6 +161,7 @@ public static function get_fields( $id, $wanted = null ) { return $a_amount <=> $b_amount; } } ); + // Store the sorted data to the cache because this is a relatively heavy operation wp_cache_set( 'sorted_meta_' . $id, $rows ); } else { @@ -168,16 +170,16 @@ public static function get_fields( $id, $wanted = null ) { $original = []; - // Convert to ordinary key-value form + // Convert the data from get_postmeta to one-dimensional key-value format for easiness foreach ( $rows as $key => $row ) { $original[ $key ] = $row[0]; } // Initiate a few arrays for the future - $times = []; - $fields = []; - $clones = []; - $layouts = []; + $times = []; + $fields = []; + $clones = []; + $layouts = []; $layout_filters = []; // Loop through all meta values @@ -196,25 +198,28 @@ public static function get_fields( $id, $wanted = null ) { // Fetch the appropriate field object $field_key = $original[ '_' . $key ]; + // Check if we have a field declaration stored in cache $field = wp_cache_get( $field_key ); if ( ! $field ) { $field = acf_get_local_field( $field_key ); // If there is a meta-meta pair but the field doesn't exist anymore, - // continue without returning the data either + // continue without returning the data if ( ! $field ) { continue; } // ACF needs this for some reason $field['_name'] = $field['name']; + + // Store the field declaration to cache wp_cache_set( $field_key, $field ); } // If there have been cloned fields, we need to run a few checks if ( ! empty( $clones ) ) { foreach ( $clones as $clone_key => $clone_value ) { - // Clone would be first in the actual key + // The clone's key would be first in the actual key $clone_key_array = explode( '_', $clone_key ); $clone_key = $clone_key_array[0]; @@ -242,7 +247,7 @@ public static function get_fields( $id, $wanted = null ) { // Create a reference to the spot where the value is supposed to be placed $value_node =& $fields; - // Reference magic + // Reference magic: find the appropriate location for the value to be stored foreach ( $path as $pkey ) { $value_node =& $value_node[ $pkey ]; } @@ -250,7 +255,7 @@ public static function get_fields( $id, $wanted = null ) { // Do field type specific things switch( $field['type'] ) { case 'clone': - // Get the cloned field's field object + // Get the cloned field's field object either from cache or from the declaration $field_object = wp_cache_get( 'local_field_' . $field['key'], 'acf' ); if ( ! $field_object ) { $field_object = acf_get_local_field( $field['key'] ); @@ -258,6 +263,7 @@ public static function get_fields( $id, $wanted = null ) { } // Loop through cloned fields and fetch their field objects + // either from cache or from the declaration foreach ( $field_object['clone'] as $cloned_fields ) { $cloned_field = wp_cache_get( $cloned_fields, 'acf' ); if ( ! $cloned_field ) { @@ -283,6 +289,8 @@ public static function get_fields( $id, $wanted = null ) { foreach ( $layouts as $index => $layout ) { // Insert the layout name in the data $value_node[ $index ] = [ 'acf_fc_layout' => $layout ]; + + // Initialize the layout's node if it isn't already there if ( ! isset( $layout_filters[ $layout ] ) ) { $layout_filters[ $layout ] = []; } @@ -298,7 +306,7 @@ public static function get_fields( $id, $wanted = null ) { $value_node = wp_cache_get( $id .'-'. $key ); if ( ! $value_node ) { - // Run the value through a bunch of filters to get the format we want + // Run the value through a bunch of ACF filters to get the format we want $value = maybe_unserialize( $value ); $value = apply_filters( "acf/format_value", $value, $id, $field ); $value = apply_filters( "acf/format_value/type={$field['type']}", $value, $id, $field ); @@ -318,7 +326,7 @@ public static function get_fields( $id, $wanted = null ) { // Sort the return array recursively by keys so that the fields are in the right order // Should be a fairly quick operation because the array should be pretty much in order already - self::ksortRecursive( $fields ); + self::ksort_recursive( $fields ); // Run DustPress Components filters for the layouts through previously stored references foreach( $layout_filters as $key => &$datas ) { @@ -337,13 +345,13 @@ public static function get_fields( $id, $wanted = null ) { * @param int $sort_flags Possible sorting flags * @return void */ - public static function ksortRecursive( &$array, $sort_flags = SORT_REGULAR ) { + public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) { if ( ! is_array( $array ) ) { return false; } ksort( $array, $sort_flags ); foreach ( $array as &$arr ) { - self::ksortRecursive( $arr, $sort_flags ); + self::ksort_recursive( $arr, $sort_flags ); } return true; } From 0d301261673c9c32de7a77b36142333b66d74f8b Mon Sep 17 00:00:00 2001 From: Miika Arponen Date: Fri, 1 Dec 2017 15:11:44 +0200 Subject: [PATCH 6/9] Better cache keys --- src/Codifier.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Codifier.php b/src/Codifier.php index 68a8eb9..111a49a 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -128,7 +128,7 @@ public static function get_fields( $id, $wanted = null ) { // Sort the meta rows so that the ones deeper in the final data tree come last // Return the sorted data from the cache if it's stored there - $sorted = wp_cache_get( 'sorted_meta_' . $id ); + $sorted = wp_cache_get( 'sorted_meta_data/' . $id ); if ( ! $sorted ) { $sort_cache = []; @@ -162,7 +162,7 @@ public static function get_fields( $id, $wanted = null ) { } } ); // Store the sorted data to the cache because this is a relatively heavy operation - wp_cache_set( 'sorted_meta_' . $id, $rows ); + wp_cache_set( 'sorted_meta_data/' . $id, $rows ); } else { $rows = $sorted; @@ -199,7 +199,7 @@ public static function get_fields( $id, $wanted = null ) { $field_key = $original[ '_' . $key ]; // Check if we have a field declaration stored in cache - $field = wp_cache_get( $field_key ); + $field = wp_cache_get( 'get_field_object=' . $field_key ); if ( ! $field ) { $field = acf_get_local_field( $field_key ); @@ -213,7 +213,7 @@ public static function get_fields( $id, $wanted = null ) { $field['_name'] = $field['name']; // Store the field declaration to cache - wp_cache_set( $field_key, $field ); + wp_cache_set( 'get_field_object=' . $field_key, $field ); } // If there have been cloned fields, we need to run a few checks @@ -256,19 +256,19 @@ public static function get_fields( $id, $wanted = null ) { switch( $field['type'] ) { case 'clone': // Get the cloned field's field object either from cache or from the declaration - $field_object = wp_cache_get( 'local_field_' . $field['key'], 'acf' ); + $field_object = wp_cache_get( 'get_field_object/' . $field['key'], 'acf' ); if ( ! $field_object ) { $field_object = acf_get_local_field( $field['key'] ); - wp_cache_set( 'local_field_' . $field['key'], $field_object, 'acf' ); + wp_cache_set( 'get_field_object/' . $field['key'], $field_object, 'acf' ); } // Loop through cloned fields and fetch their field objects // either from cache or from the declaration foreach ( $field_object['clone'] as $cloned_fields ) { - $cloned_field = wp_cache_get( $cloned_fields, 'acf' ); + $cloned_field = wp_cache_get( 'get_field_object/' . $cloned_fields, 'acf' ); if ( ! $cloned_field ) { $cloned_field = acf_get_local_field( $cloned_fields ); - wp_cache_set( $cloned_fields, $cloned_field, 'acf' ); + wp_cache_set( 'get_field_object/' . $cloned_fields, $cloned_field, 'acf' ); } // Store the objects for future use @@ -303,7 +303,7 @@ public static function get_fields( $id, $wanted = null ) { $value_node = []; break; default: - $value_node = wp_cache_get( $id .'-'. $key ); + $value_node = wp_cache_get( 'get_field/' . $id . '/' . $key ); if ( ! $value_node ) { // Run the value through a bunch of ACF filters to get the format we want @@ -313,7 +313,7 @@ public static function get_fields( $id, $wanted = null ) { $value = apply_filters( "acf/format_value/name={$field['_name']}", $value, $id, $field ); $value = apply_filters( "acf/format_value/key={$field['key']}", $value, $id, $field ); $value_node = $value; - wp_cache_set( $id .'-'. $key, $value_node ); + wp_cache_set( 'get_field/' . $id . '/' . $key, $value_node ); } break; From 8a43ca113a5e7509b01732c023e85142c4750168 Mon Sep 17 00:00:00 2001 From: Miika Arponen Date: Fri, 1 Dec 2017 15:48:45 +0200 Subject: [PATCH 7/9] Enhanced code format --- src/Codifier.php | 244 ++++++++++++++++++++++++----------------------- 1 file changed, 123 insertions(+), 121 deletions(-) diff --git a/src/Codifier.php b/src/Codifier.php index 111a49a..16173f1 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -194,134 +194,134 @@ public static function get_fields( $id, $wanted = null ) { if ( ! isset( $original[ '_' . $key ] ) ) { continue; } - else { - // Fetch the appropriate field object - $field_key = $original[ '_' . $key ]; - - // Check if we have a field declaration stored in cache - $field = wp_cache_get( 'get_field_object=' . $field_key ); - if ( ! $field ) { - $field = acf_get_local_field( $field_key ); - - // If there is a meta-meta pair but the field doesn't exist anymore, - // continue without returning the data - if ( ! $field ) { - continue; - } - - // ACF needs this for some reason - $field['_name'] = $field['name']; - // Store the field declaration to cache - wp_cache_set( 'get_field_object=' . $field_key, $field ); + // Fetch the appropriate field object + $field_key = $original[ '_' . $key ]; + + // Check if we have a field declaration stored in cache + $field = wp_cache_get( 'get_field_object=' . $field_key ); + + if ( ! $field ) { + $field = acf_get_local_field( $field_key ); + + // If there is a meta-meta pair but the field doesn't exist anymore, + // continue without returning the data + if ( ! $field ) { + continue; } - - // If there have been cloned fields, we need to run a few checks - if ( ! empty( $clones ) ) { - foreach ( $clones as $clone_key => $clone_value ) { - // The clone's key would be first in the actual key - $clone_key_array = explode( '_', $clone_key ); - $clone_key = $clone_key_array[0]; - - // Does the key start with a known clone key? - if ( substr( $key, 0, strlen( $clone_key .'_' ) ) === $clone_key .'_' ) { - $initial_path = [ $clone_key ]; - $path_key = substr( $key, strlen( $clone_key . '_' ) ); - } - else { - $path_key = $key; - $initial_path = []; - } + + // ACF needs this for some reason + $field['_name'] = $field['name']; + + // Store the field declaration to cache + wp_cache_set( 'get_field_object=' . $field_key, $field ); + } + + // If there have been cloned fields, we need to run a few checks + if ( ! empty( $clones ) ) { + foreach ( $clones as $clone_key => $clone_value ) { + // The clone's key would be first in the actual key + $clone_key_array = explode( '_', $clone_key ); + $clone_key = $clone_key_array[0]; + + // Does the key start with a known clone key? + if ( substr( $key, 0, strlen( $clone_key .'_' ) ) === $clone_key .'_' ) { + $initial_path = [ $clone_key ]; + $path_key = substr( $key, strlen( $clone_key . '_' ) ); + } + else { + $path_key = $key; + $initial_path = []; } } - else { - $path_key = $key; - $initial_path = []; - } - - // Split the key for positioning the values in the tree - $path = preg_split( '/_(\d+)_/', $path_key, 0, PREG_SPLIT_DELIM_CAPTURE ); - - $path = array_merge( $initial_path, $path ); - - // Create a reference to the spot where the value is supposed to be placed - $value_node =& $fields; - - // Reference magic: find the appropriate location for the value to be stored - foreach ( $path as $pkey ) { - $value_node =& $value_node[ $pkey ]; - } - - // Do field type specific things - switch( $field['type'] ) { - case 'clone': - // Get the cloned field's field object either from cache or from the declaration - $field_object = wp_cache_get( 'get_field_object/' . $field['key'], 'acf' ); - if ( ! $field_object ) { - $field_object = acf_get_local_field( $field['key'] ); - wp_cache_set( 'get_field_object/' . $field['key'], $field_object, 'acf' ); - } - - // Loop through cloned fields and fetch their field objects - // either from cache or from the declaration - foreach ( $field_object['clone'] as $cloned_fields ) { - $cloned_field = wp_cache_get( 'get_field_object/' . $cloned_fields, 'acf' ); - if ( ! $cloned_field ) { - $cloned_field = acf_get_local_field( $cloned_fields ); - wp_cache_set( 'get_field_object/' . $cloned_fields, $cloned_field, 'acf' ); - } - - // Store the objects for future use - $clones[ $key .'_' . $cloned_field['name'] ] = $cloned_field; - } - - // Create an empty node in the tree to store the actual values later - $value_node = []; - break; - case 'flexible_content': - // Flexible content field's value is an array of the layouts used - $layouts = maybe_unserialize( $value ); - - // Create an empty node in the tree to store the actual values - $value_node = []; - - // Loop through the layouts - foreach ( $layouts as $index => $layout ) { - // Insert the layout name in the data - $value_node[ $index ] = [ 'acf_fc_layout' => $layout ]; - - // Initialize the layout's node if it isn't already there - if ( ! isset( $layout_filters[ $layout ] ) ) { - $layout_filters[ $layout ] = []; - } - // Store a reference to the layout node for DustPress Component filtering - $layout_filters[ $layout ][] =& $value_node[ $index ]; + } + else { + $path_key = $key; + $initial_path = []; + } + + // Split the key for positioning the values in the tree + $path = preg_split( '/_(\d+)_/', $path_key, 0, PREG_SPLIT_DELIM_CAPTURE ); + + $path = array_merge( $initial_path, $path ); + + // Create a reference to the spot where the value is supposed to be placed + $value_node =& $fields; + + // Reference magic: find the appropriate location for the value to be stored + foreach ( $path as $pkey ) { + $value_node =& $value_node[ $pkey ]; + } + + // Do field type specific things + switch( $field['type'] ) { + case 'clone': + // Get the cloned field's field object either from cache or from the declaration + $field_object = wp_cache_get( 'get_field_object/' . $field['key'], 'acf' ); + if ( ! $field_object ) { + $field_object = acf_get_local_field( $field['key'] ); + wp_cache_set( 'get_field_object/' . $field['key'], $field_object, 'acf' ); + } + + // Loop through cloned fields and fetch their field objects + // either from cache or from the declaration + foreach ( $field_object['clone'] as $cloned_fields ) { + $cloned_field = wp_cache_get( 'get_field_object/' . $cloned_fields, 'acf' ); + if ( ! $cloned_field ) { + $cloned_field = acf_get_local_field( $cloned_fields ); + wp_cache_set( 'get_field_object/' . $cloned_fields, $cloned_field, 'acf' ); } - break; - case 'repeater': - // Create an empty node in the tree to store the actual values later - $value_node = []; - break; - default: - $value_node = wp_cache_get( 'get_field/' . $id . '/' . $key ); - - if ( ! $value_node ) { - // Run the value through a bunch of ACF filters to get the format we want - $value = maybe_unserialize( $value ); - $value = apply_filters( "acf/format_value", $value, $id, $field ); - $value = apply_filters( "acf/format_value/type={$field['type']}", $value, $id, $field ); - $value = apply_filters( "acf/format_value/name={$field['_name']}", $value, $id, $field ); - $value = apply_filters( "acf/format_value/key={$field['key']}", $value, $id, $field ); - $value_node = $value; - wp_cache_set( 'get_field/' . $id . '/' . $key, $value_node ); + + // Store the objects for future use + $clones[ $key .'_' . $cloned_field['name'] ] = $cloned_field; + } + + // Create an empty node in the tree to store the actual values later + $value_node = []; + break; + case 'flexible_content': + // Flexible content field's value is an array of the layouts used + $layouts = maybe_unserialize( $value ); + + // Create an empty node in the tree to store the actual values + $value_node = []; + + // Loop through the layouts + foreach ( $layouts as $index => $layout ) { + // Insert the layout name in the data + $value_node[ $index ] = [ 'acf_fc_layout' => $layout ]; + + // Initialize the layout's node if it isn't already there + if ( ! isset( $layout_filters[ $layout ] ) ) { + $layout_filters[ $layout ] = []; } - break; - - } - - // Unset the reference to prevent odd bugs🐛 to appear - unset( $value_node ); + // Store a reference to the layout node for DustPress Component filtering + $layout_filters[ $layout ][] =& $value_node[ $index ]; + } + break; + case 'repeater': + // Create an empty node in the tree to store the actual values later + $value_node = []; + break; + default: + $value_node = wp_cache_get( 'get_field/' . $id . '/' . $key ); + + if ( ! $value_node ) { + // Run the value through a bunch of ACF filters to get the format we want + $value = maybe_unserialize( $value ); + $value = apply_filters( "acf/format_value", $value, $id, $field ); + $value = apply_filters( "acf/format_value/type={$field['type']}", $value, $id, $field ); + $value = apply_filters( "acf/format_value/name={$field['_name']}", $value, $id, $field ); + $value = apply_filters( "acf/format_value/key={$field['key']}", $value, $id, $field ); + $value_node = $value; + wp_cache_set( 'get_field/' . $id . '/' . $key, $value_node ); + } + break; + } + + // Unset the reference to prevent odd bugs🐛 to appear + unset( $value_node ); } // Sort the return array recursively by keys so that the fields are in the right order @@ -349,10 +349,12 @@ public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) { if ( ! is_array( $array ) ) { return false; } + ksort( $array, $sort_flags ); foreach ( $array as &$arr ) { self::ksort_recursive( $arr, $sort_flags ); } + return true; } } From da4082cd6fce793c1fc49488b11e0ab9ff582f40 Mon Sep 17 00:00:00 2001 From: Ville Siltala Date: Wed, 13 Dec 2017 17:16:41 +0200 Subject: [PATCH 8/9] Cache flush control in progress.. --- plugin.php | 2 +- src/Codifier.php | 159 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 124 insertions(+), 37 deletions(-) diff --git a/plugin.php b/plugin.php index 7e88cee..d91a312 100644 --- a/plugin.php +++ b/plugin.php @@ -3,7 +3,7 @@ Plugin Name: ACF Codifier Plugin URI: https://github.com/devgeniem/acf-codifier Description: A helper class to make defining ACF field groups and fields easier in the code. -Version: 1.2.0-beta +Version: 1.2.0 Author: Geniem Oy Author URI: https://geniem.fi License: GPLv2 diff --git a/src/Codifier.php b/src/Codifier.php index 16173f1..25be444 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -16,12 +16,54 @@ class Codifier { protected static $hidden_labels = []; /** - * Init function for registering actions + * This is used with WP object caching. + * We use ACF's cache group for now. + * + * This is overridable with the 'ACF_CODIFIER_CACHE_GROUP' constant. + * + * @var string + */ + public static $cache_group = 'acf'; + + /** + * This ttl is used when objects are not eternally cached. + * + * This is overridable with the 'ACF_CODIFIER_CACHE_TTL' constant. + * + * @var float|int + */ + public static $cache_ttl = 15 * MINUTE_IN_SECONDS; + + /** + * Initialize the plugin by adding hooks. * * @return void */ public static function init() { + + self::override_settings(); + add_action( 'admin_head', __CLASS__ . '::hide_labels' ); + + // Cache control. + add_action( 'updated_postmeta', [ __CLASS__, 'flush_post_meta_caches' ], 1, 1 ); + add_filter( 'acf/update_value', [ __CLASS__, 'flush_formatted_value' ], 10, 3 ); + + } + + /** + * This function overrides class settings with defined constants. + */ + public static function override_settings( ) { + + if ( defined( 'ACF_CODIFIER_CACHE_GROUP' ) ) { + self::$cache_group = ACF_CODIFIER_CACHE_GROUP; + } + + if ( defined( 'ACF_CODIFIER_CACHE_TTL' ) ) { + self::$cache_group = ACF_CODIFIER_CACHE_TTL; + } + } /** @@ -110,7 +152,7 @@ public static function get_label_visibility( $field ) { */ public static function get_fields( $id, $wanted = null ) { global $wpdb; - + // Get the raw meta data from the database $rows = get_post_meta( $id ); @@ -125,10 +167,10 @@ public static function get_fields( $id, $wanted = null ) { return in_array( $key, $wanted ); }, ARRAY_FILTER_USE_KEY ); } - + // Sort the meta rows so that the ones deeper in the final data tree come last // Return the sorted data from the cache if it's stored there - $sorted = wp_cache_get( 'sorted_meta_data/' . $id ); + $sorted = wp_cache_get( 'sorted_meta_data/' . $id, self::$cache_group ); if ( ! $sorted ) { $sort_cache = []; @@ -143,7 +185,7 @@ public static function get_fields( $id, $wanted = null ) { preg_match_all( '/_(\d+)_/', $a, $a_amount ); $a_amount = count( $a_amount[0] ); } - + // Check if $b parse value is in cache if ( isset( $sort_cache[ $b ] ) ) { $b_amount = $sort_cache[ $b ]; @@ -157,31 +199,32 @@ public static function get_fields( $id, $wanted = null ) { // If the depth is same, sort alphabetically if ( $a_amount === $b_amount ) { return $a <=> $b; - } else { + } + else { return $a_amount <=> $b_amount; } } ); // Store the sorted data to the cache because this is a relatively heavy operation - wp_cache_set( 'sorted_meta_data/' . $id, $rows ); + wp_cache_set( 'sorted_meta_data/' . $id, $rows, self::$cache_group ); } else { $rows = $sorted; } - + $original = []; - + // Convert the data from get_postmeta to one-dimensional key-value format for easiness foreach ( $rows as $key => $row ) { $original[ $key ] = $row[0]; } - + // Initiate a few arrays for the future $times = []; $fields = []; $clones = []; $layouts = []; $layout_filters = []; - + // Loop through all meta values foreach ( $original as $key => $value ) { @@ -189,18 +232,18 @@ public static function get_fields( $id, $wanted = null ) { if ( $key[0] === '_' ) { continue; } - + // If a field doesn't have a meta-meta pair, we don't want to include it if ( ! isset( $original[ '_' . $key ] ) ) { continue; } - // Fetch the appropriate field object + // Fetch the appropriate field object $field_key = $original[ '_' . $key ]; // Check if we have a field declaration stored in cache - $field = wp_cache_get( 'get_field_object=' . $field_key ); - + $field = wp_cache_get( 'get_field_object=' . $field_key, self::$cache_group ); + if ( ! $field ) { $field = acf_get_local_field( $field_key ); @@ -214,7 +257,7 @@ public static function get_fields( $id, $wanted = null ) { $field['_name'] = $field['name']; // Store the field declaration to cache - wp_cache_set( 'get_field_object=' . $field_key, $field ); + wp_cache_set( 'get_field_object=' . $field_key, $field, self::$cache_group ); } // If there have been cloned fields, we need to run a few checks @@ -222,21 +265,21 @@ public static function get_fields( $id, $wanted = null ) { foreach ( $clones as $clone_key => $clone_value ) { // The clone's key would be first in the actual key $clone_key_array = explode( '_', $clone_key ); - $clone_key = $clone_key_array[0]; + $clone_key = $clone_key_array[0]; // Does the key start with a known clone key? - if ( substr( $key, 0, strlen( $clone_key .'_' ) ) === $clone_key .'_' ) { + if ( substr( $key, 0, strlen( $clone_key . '_' ) ) === $clone_key . '_' ) { $initial_path = [ $clone_key ]; - $path_key = substr( $key, strlen( $clone_key . '_' ) ); + $path_key = substr( $key, strlen( $clone_key . '_' ) ); } else { - $path_key = $key; + $path_key = $key; $initial_path = []; } } } else { - $path_key = $key; + $path_key = $key; $initial_path = []; } @@ -253,27 +296,40 @@ public static function get_fields( $id, $wanted = null ) { $value_node =& $value_node[ $pkey ]; } + /** + * Filters the cache group key before field handling. + * + * Use this to set specific group keys for different post types for instance. + * + * @since 1.2.0 + * + * @param string $cache_group The default group key. + * @param int $id The post id. + * @param string $key The ACF field key. + */ + $cache_group = apply_filters( 'acf_codifier_cache_key', self::$cache_group, $id, $field['key'] ); + // Do field type specific things - switch( $field['type'] ) { + switch ( $field['type'] ) { case 'clone': // Get the cloned field's field object either from cache or from the declaration - $field_object = wp_cache_get( 'get_field_object/' . $field['key'], 'acf' ); + $field_object = wp_cache_get( 'get_field_object/' . $field['key'], $cache_group ); if ( ! $field_object ) { $field_object = acf_get_local_field( $field['key'] ); - wp_cache_set( 'get_field_object/' . $field['key'], $field_object, 'acf' ); + wp_cache_set( 'get_field_object/' . $field['key'], $field_object, $cache_group ); } // Loop through cloned fields and fetch their field objects // either from cache or from the declaration foreach ( $field_object['clone'] as $cloned_fields ) { - $cloned_field = wp_cache_get( 'get_field_object/' . $cloned_fields, 'acf' ); + $cloned_field = wp_cache_get( 'get_field_object/' . $cloned_fields, $cache_group ); if ( ! $cloned_field ) { $cloned_field = acf_get_local_field( $cloned_fields ); - wp_cache_set( 'get_field_object/' . $cloned_fields, $cloned_field, 'acf' ); + wp_cache_set( 'get_field_object/' . $cloned_fields, $cloned_field, $cache_group ); } // Store the objects for future use - $clones[ $key .'_' . $cloned_field['name'] ] = $cloned_field; + $clones[ $key . '_' . $cloned_field['name'] ] = $cloned_field; } // Create an empty node in the tree to store the actual values later @@ -304,9 +360,10 @@ public static function get_fields( $id, $wanted = null ) { $value_node = []; break; default: - $value_node = wp_cache_get( 'get_field/' . $id . '/' . $key ); + $value_node = wp_cache_get( 'get_field/formatted/' . $id . '/' . $key, $cache_group ); if ( ! $value_node ) { + // Run the value through a bunch of ACF filters to get the format we want $value = maybe_unserialize( $value ); $value = apply_filters( "acf/format_value", $value, $id, $field ); @@ -314,36 +371,43 @@ public static function get_fields( $id, $wanted = null ) { $value = apply_filters( "acf/format_value/name={$field['_name']}", $value, $id, $field ); $value = apply_filters( "acf/format_value/key={$field['key']}", $value, $id, $field ); $value_node = $value; - wp_cache_set( 'get_field/' . $id . '/' . $key, $value_node ); + + // Store the formatted value. This is stored for a shorter time to prevent data incoherent data. + wp_cache_set( + 'get_field/formatted/' . $id . '/' . $key, + $value_node, + $cache_group, + self::$cache_ttl + ); } break; } - // Unset the reference to prevent odd bugs🐛 to appear + // Unset the reference to prevent odd bugs to appear unset( $value_node ); } - + // Sort the return array recursively by keys so that the fields are in the right order // Should be a fairly quick operation because the array should be pretty much in order already self::ksort_recursive( $fields ); - + // Run DustPress Components filters for the layouts through previously stored references foreach( $layout_filters as $key => &$datas ) { foreach( $datas as &$data ) { $data = apply_filters( 'dustpress/components/data=' . $key, $data ); } } - + return $fields; } - + /** * A helper method to sort arrays recursively by key * * @param array $array An array to sort * @param int $sort_flags Possible sorting flags - * @return void + * @return boolean */ public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) { if ( ! is_array( $array ) ) { @@ -354,7 +418,30 @@ public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) { foreach ( $array as &$arr ) { self::ksort_recursive( $arr, $sort_flags ); } - + return true; } + + /** + * Clears the cache for sorted meta rows. + * This is done after any meta value is changed. + * + * @param int $post_id Post ID. + */ + public static function flush_sorted_meta( $post_id ) { + wp_cache_delete( 'sorted_meta_data/' . $post_id, self::$cache_group ); + } + + /** + * Flus a single value cache if its value is changed. + * + * @link https://www.advancedcustomfields.com/resources/acf-update_value/ + * + * @param mixed $value + * @param int $post_id + * @param object $field + */ + public static function flush_formatted_value( $value, $post_id, $field ) { + + } } From ba143e9036efa184070e041c98774f9c410fe47a Mon Sep 17 00:00:00 2001 From: Ville Siltala Date: Fri, 15 Dec 2017 17:00:07 +0200 Subject: [PATCH 9/9] Added cache flushing for meta data caching in get_fields(). --- CHANGELOG.md | 4 + src/Codifier.php | 188 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 131 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 327636c..f8b72cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Added a more effective version of ACF's get_fields() method to use with the Codifier + - Cache ttl is set to 15 minutes by default and it can be changed + with the `ACF_CODIFIER_CACHE_TTL` constant or through the `acf_codifier_cache_ttl` filter. + - Meta value caches are flushed after any meta value is updated. +- Codifier settings which are overridable with constants. ## [1.1.3] - 2017-11-27 diff --git a/src/Codifier.php b/src/Codifier.php index 25be444..cd7c2f5 100644 --- a/src/Codifier.php +++ b/src/Codifier.php @@ -16,23 +16,11 @@ class Codifier { protected static $hidden_labels = []; /** - * This is used with WP object caching. - * We use ACF's cache group for now. + * This array holds all plugin settings which are overridable with constants. * - * This is overridable with the 'ACF_CODIFIER_CACHE_GROUP' constant. - * - * @var string - */ - public static $cache_group = 'acf'; - - /** - * This ttl is used when objects are not eternally cached. - * - * This is overridable with the 'ACF_CODIFIER_CACHE_TTL' constant. - * - * @var float|int + * @var array */ - public static $cache_ttl = 15 * MINUTE_IN_SECONDS; + protected static $settings = []; /** * Initialize the plugin by adding hooks. @@ -41,29 +29,75 @@ class Codifier { */ public static function init() { - self::override_settings(); + self::load_settings(); add_action( 'admin_head', __CLASS__ . '::hide_labels' ); // Cache control. - add_action( 'updated_postmeta', [ __CLASS__, 'flush_post_meta_caches' ], 1, 1 ); - add_filter( 'acf/update_value', [ __CLASS__, 'flush_formatted_value' ], 10, 3 ); + add_action( 'updated_postmeta', [ __CLASS__, 'flush_meta_cache' ], 1, 3 ); } /** - * This function overrides class settings with defined constants. + * This function loads the plugin settings and possibly overrides them with constants. */ - public static function override_settings( ) { + protected static function load_settings() { + + /** + * This is the WP object cache group key for field objects. + * + * This is overridable with the 'ACF_CODIFIER_FIELD_OBJECT_GROUP' constant. + * + * @var string Defaults to 'acf_codifier/field_object'. + */ + self::$settings['field_object_group'] = defined( 'ACF_CODIFIER_FIELD_OBJECT_GROUP' ) ? + ACF_CODIFIER_FIELD_OBJECT_GROUP : 'acf_codifier/field_object'; + + /** + * This is the WP object cache group key for objects' sorted metadata. + * + * This is overridable with the 'ACF_CODIFIER_SORTED_META_GROUP' constant. + * + * @var string Defaults to 'acf_codifier/sorted_meta'. + */ + self::$settings['sorted_meta_group'] = defined( 'ACF_CODIFIER_SORTED_META_GROUP' ) ? + ACF_CODIFIER_SORTED_META_GROUP : 'acf_codifier/sorted_meta'; + + /** + * This is the WP object cache group key loaded field values. + * + * This is overridable with the 'ACF_CODIFIER_FIELD_VALUE' constant. + * + * @var string Defaults to 'acf_codifier/field_value'. + */ + self::$settings['field_value_group'] = defined( 'ACF_CODIFIER_FIELD_VALUE' ) ? + ACF_CODIFIER_FIELD_VALUE : 'acf_codifier/field_value'; + + /** + * This ttl is the default ttl for object caching in get_fields() method. + * + * This is overridable with the 'ACF_CODIFIER_CACHE_TTL' constant. + * + * @var float|int Defaults to 15 minutes in seconds. + */ + self::$settings['cache_ttl'] = defined( 'ACF_CODIFIER_CACHE_TTL' ) ? + ACF_CODIFIER_CACHE_TTL : 15 * MINUTE_IN_SECONDS; - if ( defined( 'ACF_CODIFIER_CACHE_GROUP' ) ) { - self::$cache_group = ACF_CODIFIER_CACHE_GROUP; - } + } - if ( defined( 'ACF_CODIFIER_CACHE_TTL' ) ) { - self::$cache_group = ACF_CODIFIER_CACHE_TTL; + /** + * Get a plugin setting by a passed key. + * + * @param string $key The setting key. + * + * @return mixed|null The found setting or null value. + */ + public static function get_setting( $key ) { + if ( isset( self::$settings[ $key ] ) ) { + return self::$settings[ $key ]; } + return null; } /** @@ -146,13 +180,25 @@ public static function get_label_visibility( $field ) { /** * A more efficient replacement for ACF's native get_fields function * - * @param int $id Post id to fetch fields from - * @param array $wanted Keys for wanted fields. Empty or null default to all + * @param int $id Post id to fetch fields from. If no id is passed, we get it with get_the_ID(). + * @param array $wanted Keys for wanted fields. Empty or null default to all. + * * @return array Fields as an associative array */ - public static function get_fields( $id, $wanted = null ) { + public static function get_fields( $id = 0, $wanted = null ) { global $wpdb; + // Settings needed. + $sorted_meta_group = self::$settings['sorted_meta_group']; + $field_object_group = self::$settings['field_object_group']; + $field_value_group = self::$settings['field_value_group']; + $cache_ttl = self::$settings['cache_ttl']; + + // If no id is set, get the current id. + if ( $id === 0 ) { + $id = get_the_ID(); + } + // Get the raw meta data from the database $rows = get_post_meta( $id ); @@ -170,7 +216,7 @@ public static function get_fields( $id, $wanted = null ) { // Sort the meta rows so that the ones deeper in the final data tree come last // Return the sorted data from the cache if it's stored there - $sorted = wp_cache_get( 'sorted_meta_data/' . $id, self::$cache_group ); + $sorted = wp_cache_get( 'sorted_meta_data/' . $id, $sorted_meta_group ); if ( ! $sorted ) { $sort_cache = []; @@ -205,7 +251,7 @@ public static function get_fields( $id, $wanted = null ) { } } ); // Store the sorted data to the cache because this is a relatively heavy operation - wp_cache_set( 'sorted_meta_data/' . $id, $rows, self::$cache_group ); + wp_cache_set( 'sorted_meta_data/' . $id, $rows, $sorted_meta_group ); } else { $rows = $sorted; @@ -242,7 +288,7 @@ public static function get_fields( $id, $wanted = null ) { $field_key = $original[ '_' . $key ]; // Check if we have a field declaration stored in cache - $field = wp_cache_get( 'get_field_object=' . $field_key, self::$cache_group ); + $field = wp_cache_get( 'get_field_object=' . $field_key, $field_object_group ); if ( ! $field ) { $field = acf_get_local_field( $field_key ); @@ -257,7 +303,7 @@ public static function get_fields( $id, $wanted = null ) { $field['_name'] = $field['name']; // Store the field declaration to cache - wp_cache_set( 'get_field_object=' . $field_key, $field, self::$cache_group ); + wp_cache_set( 'get_field_object=' . $field_key, $field, $field_object_group ); } // If there have been cloned fields, we need to run a few checks @@ -297,35 +343,35 @@ public static function get_fields( $id, $wanted = null ) { } /** - * Filters the cache group key before field handling. + * Filters the cache ttl before field handling. * - * Use this to set specific group keys for different post types for instance. + * Use this to set specific ttl for different post types for instance. * * @since 1.2.0 * - * @param string $cache_group The default group key. - * @param int $id The post id. - * @param string $key The ACF field key. + * @param int $field_value_ttl The cache ttl in seconds. + * @param int $id The post id. + * @param string $field The ACF field object. */ - $cache_group = apply_filters( 'acf_codifier_cache_key', self::$cache_group, $id, $field['key'] ); + $field_value_ttl = apply_filters( 'acf_codifier_cache_ttl', $cache_ttl, $id, $field ); // Do field type specific things switch ( $field['type'] ) { case 'clone': // Get the cloned field's field object either from cache or from the declaration - $field_object = wp_cache_get( 'get_field_object/' . $field['key'], $cache_group ); + $field_object = wp_cache_get( 'get_field_object/' . $field['key'], $field_object_group ); if ( ! $field_object ) { $field_object = acf_get_local_field( $field['key'] ); - wp_cache_set( 'get_field_object/' . $field['key'], $field_object, $cache_group ); + wp_cache_set( 'get_field_object/' . $field['key'], $field_object, $field_object_group, $cache_ttl ); } // Loop through cloned fields and fetch their field objects // either from cache or from the declaration foreach ( $field_object['clone'] as $cloned_fields ) { - $cloned_field = wp_cache_get( 'get_field_object/' . $cloned_fields, $cache_group ); + $cloned_field = wp_cache_get( 'get_field_object/' . $cloned_fields, $field_object_group ); if ( ! $cloned_field ) { $cloned_field = acf_get_local_field( $cloned_fields ); - wp_cache_set( 'get_field_object/' . $cloned_fields, $cloned_field, $cache_group ); + wp_cache_set( 'get_field_object/' . $cloned_fields, $cloned_field, $field_object_group, $cache_ttl ); } // Store the objects for future use @@ -360,7 +406,7 @@ public static function get_fields( $id, $wanted = null ) { $value_node = []; break; default: - $value_node = wp_cache_get( 'get_field/formatted/' . $id . '/' . $key, $cache_group ); + $value_node = wp_cache_get( 'get_field/' . $id . '/' . $key, $field_value_group ); if ( ! $value_node ) { @@ -374,10 +420,10 @@ public static function get_fields( $id, $wanted = null ) { // Store the formatted value. This is stored for a shorter time to prevent data incoherent data. wp_cache_set( - 'get_field/formatted/' . $id . '/' . $key, + 'get_field/' . $id . '/' . $key, $value_node, - $cache_group, - self::$cache_ttl + $field_value_group, + $field_value_ttl ); } break; @@ -393,8 +439,8 @@ public static function get_fields( $id, $wanted = null ) { self::ksort_recursive( $fields ); // Run DustPress Components filters for the layouts through previously stored references - foreach( $layout_filters as $key => &$datas ) { - foreach( $datas as &$data ) { + foreach ( $layout_filters as $key => &$datas ) { + foreach ( $datas as &$data ) { $data = apply_filters( 'dustpress/components/data=' . $key, $data ); } } @@ -405,8 +451,8 @@ public static function get_fields( $id, $wanted = null ) { /** * A helper method to sort arrays recursively by key * - * @param array $array An array to sort - * @param int $sort_flags Possible sorting flags + * @param array $array An array to sort. + * @param int $sort_flags Possible sorting flags. * @return boolean */ public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) { @@ -423,25 +469,45 @@ public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) { } /** - * Clears the cache for sorted meta rows. - * This is done after any meta value is changed. + * Controls cache flushing when a post meta value is changed. + * Flushes the sorted meta and the field value cache. + * + * @since 1.2.0 * - * @param int $post_id Post ID. + * @param int $meta_id ID of updated metadata entry. + * @param int $object_id Object ID. + * @param string $meta_key Meta key. + * + * @return null */ - public static function flush_sorted_meta( $post_id ) { - wp_cache_delete( 'sorted_meta_data/' . $post_id, self::$cache_group ); + public static function flush_meta_cache( $meta_id, $object_id, $meta_key ) { + + /** + * Skip the edit lock meta key which controls post locking. + * + * @link https://codex.wordpress.org/Post_Locking + */ + if ( $meta_key === '_edit_lock' ) { + return; + } + + wp_cache_delete( 'sorted_meta_data/' . $object_id, self::$settings['sorted_meta_group'] ); + + self::flush_field_value( $object_id, $meta_key ); } /** - * Flus a single value cache if its value is changed. + * Flushes the cache of a single field value if its meta value is updated. + * + * Note that a meta key might not match to a ACF field value. In this case + * WP object cache will not find a matching cache key and nothing is done. * * @link https://www.advancedcustomfields.com/resources/acf-update_value/ * - * @param mixed $value - * @param int $post_id - * @param object $field + * @param int $id The object id. + * @param array $meta_key The post meta key of the field value. */ - public static function flush_formatted_value( $value, $post_id, $field ) { - + public static function flush_field_value( $id, $meta_key ) { + wp_cache_delete( 'get_field/' . $id . '/' . $meta_key, self::$settings['field_value_group'] ); } }