HEX
Server: Apache/2.4.66 (Debian)
System: Linux 6dfabc3b2241 6.8.0-71-generic #71-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 22 16:52:38 UTC 2025 x86_64
User: (1000)
PHP: 8.3.30
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/wpgraphql-acf/src/Utils.php
<?php
namespace WPGraphQL\Acf;

use WPGraphQL\Acf\Model\AcfOptionsPage;
use WPGraphQL\Model\Comment;
use WPGraphQL\Model\Menu;
use WPGraphQL\Model\MenuItem;
use WPGraphQL\Model\Post;
use WPGraphQL\Model\Term;
use WPGraphQL\Model\User;

class Utils {

	/**
	 * @var \WPGraphQL\Acf\FieldTypeRegistry|null
	 */
	protected static $type_registry;

	/**
	 * @param mixed $node
	 *
	 * @return int|mixed|string
	 */
	public static function get_node_acf_id( $node ) {

		/**
		 * If a value is returned from this filter,
		 */
		$pre_get_node_acf_id = apply_filters( 'wpgraphql/acf/pre_get_node_acf_id', null, $node );

		if ( null !== $pre_get_node_acf_id ) {
			return $pre_get_node_acf_id;
		}

		if ( is_array( $node ) && isset( $node['node']->ID ) ) {
			return absint( $node['node']->ID );
		}

		switch ( true ) {
			case $node instanceof Term:
				$id = 'term_' . $node->term_id;
				break;
			case $node instanceof Post:
				$id = absint( $node->databaseId );
				break;
			case $node instanceof MenuItem:
				$id = absint( $node->databaseId );
				break;
			case $node instanceof Menu:
				$id = 'term_' . $node->databaseId;
				break;
			case $node instanceof User:
				$id = 'user_' . absint( $node->databaseId );
				break;
			case $node instanceof Comment:
				$id = 'comment_' . absint( $node->databaseId );
				break;
			case $node instanceof AcfOptionsPage:
				$id = $node->acfId;
				break;
			case is_array( $node ) && isset( $node['post_id'] ):
				$id = $node['post_id'];
				break;
			default:
				$id = 0;
				break;
		}

		return $id;
	}

	/**
	 * Clear the Type Registry for tests
	 */
	public static function clear_field_type_registry(): void {
		self::$type_registry = null;
	}

	/**
	 * Return the Field Type Registry instance
	 */
	public static function get_type_registry(): FieldTypeRegistry {
		if ( self::$type_registry instanceof FieldTypeRegistry ) {
			return self::$type_registry;
		}

		self::$type_registry = new FieldTypeRegistry();
		return self::$type_registry;
	}

	/**
	 * Given the name of an ACF Field Type (text, textarea, etc) return the AcfGraphQLFieldType definition
	 *
	 * @param string $acf_field_type The name of the ACF Field Type (text, textarea, etc)
	 */
	public static function get_graphql_field_type( string $acf_field_type ): ?AcfGraphQLFieldType {
		return self::get_type_registry()->get_field_type( $acf_field_type );
	}

	/**
	 * Get a list of supported fields that WPGraphQL for ACF supports.
	 *
	 * This is helpful for determining whether UI should be output for the field, and whether
	 * the field should be added to the Schema.
	 *
	 * Some fields, such as "Accordion" are not supported currently.
	 *
	 * @return array<mixed>
	 */
	public static function get_supported_acf_fields_types(): array {
		$registry               = self::get_type_registry();
		$registered_fields      = $registry->get_registered_field_types();
		$registered_field_names = array_keys( $registered_fields );

		/**
		 * Filter the supported fields
		 *
		 * @param array $supported_fields
		 */
		return apply_filters( 'wpgraphql/acf/supported_field_types', $registered_field_names );
	}

	/**
	 * Returns an array of ACF Options Pages that are set to show in the graphql schema
	 *
	 * @return array<mixed>
	 */
	public static function get_acf_options_pages(): array {
		$options_pages = [];
		if ( ! function_exists( 'acf_get_options_pages' ) ) {
			return $options_pages;
		}
		$acf_options_pages = acf_get_options_pages();

		if ( empty( $acf_options_pages ) || ! is_array( $acf_options_pages ) ) {
			return $options_pages;
		}

		// For programmatically registered options pages, ACF may not preserve show_in_graphql.
		// Allow it to be filtered to restore the value if it was set during registration.
		$acf_options_pages = array_map(
			static function ( $option_page ) {
				// If show_in_graphql is not set, allow it to be filtered to restore the value
				// This allows tests and programmatic registrations to set show_in_graphql => false
				if ( ! isset( $option_page['show_in_graphql'] ) ) {
					$option_page['show_in_graphql'] = apply_filters( 'wpgraphql/acf/options_page/show_in_graphql', true, $option_page );
				}
				return $option_page;
			},
			$acf_options_pages
		);

		return array_filter(
			array_map(
				static function ( $option_page ) {
					return self::should_field_group_show_in_graphql( $option_page ) ? $option_page : null;
				},
				$acf_options_pages
			)
		);
	}

	/**
	 * Returns all available GraphQL Types
	 *
	 * @return array<mixed>
	 * @throws \Exception
	 */
	public static function get_all_graphql_types(): array {
		$graphql_types = [];

		// Use GraphQL to get the Interface and the Types that implement them
		$query = '
		query GetPossibleTypes($name:String!){
			__type(name:$name){
				name
				description
				possibleTypes {
					name
					description
				}
			}
		}
		';

		$interfaces = [
			'ContentNode'     => [
				'label'        => __( 'Post Type', 'wpgraphql-acf' ),
				'plural_label' => __( 'All Post Types', 'wpgraphql-acf' ),
			],
			'TermNode'        => [
				'label'        => __( 'Taxonomy', 'wpgraphql-acf' ),
				'plural_label' => __( 'All Taxonomies', 'wpgraphql-acf' ),
			],
			'ContentTemplate' => [
				'label'        => __( 'Page Template', 'wpgraphql-acf' ),
				'plural_label' => __( 'All Templates Assignable to Content', 'wpgraphql-acf' ),
			],
		];

		if ( function_exists( 'acf_get_options_pages' ) && ! empty( acf_get_options_pages() ) ) {
			$interfaces['AcfOptionsPage'] = [
				'label'        => __( 'ACF Options Page', 'wpgraphql-acf' ),
				'plural_label' => __( 'All Options Pages registered by ACF', 'wpgraphql-acf' ),
			];
		}

		/**
		 * @param array $interfaces Array of Interfaces to include in the possible types that an ACF Field could be associated with
		 */
		$interfaces = apply_filters( 'wpgraphql/acf/get_all_possible_types/interfaces', $interfaces );

		foreach ( $interfaces as $interface_name => $config ) {
			$interface_query = graphql(
				[
					'query'     => $query,
					'variables' => [
						'name' => $interface_name,
					],
				]
			);

			$possible_types = is_array( $interface_query ) && ! empty( $interface_query['data']['__type']['possibleTypes'] ) ? $interface_query['data']['__type']['possibleTypes'] : [];

			if ( empty( $possible_types ) ) {
				continue;
			}

			asort( $possible_types );

			// Intentionally not translating "ContentNode Interface" as this is part of the GraphQL Schema and should not be translated.
			$graphql_types[ $interface_name ] = '<span data-interface="' . $interface_name . '">' . $interface_name . ' Interface (' . $config['plural_label'] . ')</span>';
			$label                            = '<span data-implements="' . $interface_name . '"> (' . $config['label'] . ')</span>';
			foreach ( $possible_types as $type ) {
				$type_label = $type['name'] . '&nbsp;' . $label;
				$type_key   = $type['name'];

				$graphql_types[ $type_key ] = $type_label;
			}
		}

		/**
		 * Add comment to GraphQL types
		 */
		$graphql_types['Comment'] = __( 'Comment', 'wpgraphql-acf' );

		/**
		 * Add menu to GraphQL types
		 */
		$graphql_types['Menu'] = __( 'Menu', 'wpgraphql-acf' );

		/**
		 * Add menu items to GraphQL types
		 */
		$graphql_types['MenuItem'] = __( 'Menu Item', 'wpgraphql-acf' );

		/**
		 * Add users to GraphQL types
		 */
		$graphql_types['User'] = __( 'User', 'wpgraphql-acf' );

		return $graphql_types;
	}

	/**
	 * Whether the ACF Field Group should show in the GraphQL Schema
	 *
	 * @param array<mixed> $acf_field
	 */
	public static function should_field_show_in_graphql( array $acf_field ): bool {
		return self::should_field_group_show_in_graphql( $acf_field );
	}

	/**
	 * Whether the ACF Field Group should show in the GraphQL Schema
	 *
	 * @param array<mixed> $acf_field_group
	 */
	public static function should_field_group_show_in_graphql( array $acf_field_group ): bool {
		$should = true;

		$show_in_rest = $acf_field_group['show_in_rest'] ?? false;


		// if the field group was configured with no "show_in_graphql" value, default to the "show_in_rest" value
		// to determine if the group should be available in an API
		if (
			( isset( $acf_field_group['is_options_page'] ) && false === $acf_field_group['is_options_page'] ) &&
			! isset( $acf_field_group['show_in_graphql'] ) ) {
			$acf_field_group['show_in_graphql'] = $show_in_rest;
		}

		if ( isset( $acf_field_group['show_in_graphql'] ) && false === (bool) $acf_field_group['show_in_graphql'] ) {
			$should = false;
		}


		return (bool) apply_filters( 'wpgraphql/acf/should_field_group_show_in_graphql', $should, $acf_field_group );
	}

	/**
	 * Given a field group config, return the name of the field group to be used in the GraphQL
	 * Schema
	 *
	 * @param array<mixed> $field_group The field group config array
	 */
	public static function get_field_group_name( array $field_group ): string {
		$field_group_name = '';

		if ( ! empty( $field_group['graphql_field_name'] ) ) {
			$field_group_name = $field_group['graphql_field_name'];
			$field_group_name = preg_replace( '/[^0-9a-zA-Z_\s]/i', '', $field_group_name );
		} elseif ( ! empty( $field_group['graphql_type_name'] ) ) {
			$field_group_name = \WPGraphQL\Utils\Utils::format_field_name( $field_group['graphql_type_name'], true );
		} else {
			if ( ! empty( $field_group['name'] ) ) {
				$field_group_name = $field_group['name'];
			} elseif ( ! empty( $field_group['title'] ) ) {
				$field_group_name = $field_group['title'];
			} elseif ( ! empty( $field_group['label'] ) ) {
				$field_group_name = $field_group['label'];
			} elseif ( ! empty( $field_group['page_title'] ) ) {
				$field_group_name = $field_group['page_title'];
			}
			$field_group_name = preg_replace( '/[^0-9a-zA-Z_\s]/i', ' ', $field_group_name );
			// if the graphql_field_name isn't explicitly defined, we'll format it without underscores
			$field_group_name = \WPGraphQL\Utils\Utils::format_field_name( $field_group_name, false );
		}

		if ( empty( $field_group_name ) ) {
			return $field_group_name;
		}

		$starts_with_string = is_numeric( substr( $field_group_name, 0, 1 ) );

		if ( $starts_with_string ) {
			graphql_debug(
				__( 'The ACF Field or Field Group could not be added to the schema. GraphQL Field and Type names cannot start with a number', 'wpgraphql-acf' ),
				[
					'invalid' => $field_group,
				]
			);
			return '';
		}

		return $field_group_name;
	}

	/**
	 * Returns string of the items in the array list. Limit allows string to be limited length.
	 *
	 * @param array<mixed> $items
	 * @param int          $limit
	 */
	public static function array_list_by_limit( array $items, int $limit = 5 ): string {
		$flat_list = '';
		$total     = count( $items );

		// Labels.
		$labels     = $items;
		$labels     = array_slice( $labels, 0, $limit );
		$flat_list .= implode( ', ', $labels );

		// More.
		if ( $total > $limit ) {
			$flat_list .= ', ...';
		}
		return $flat_list;
	}
}