wppaste
WordPress

get_post_types( array|string $args = array(), string $output = 'names', string $operator = 'and' ): string[]|WP_Post_Type[]

Since
2.9.0
Source
wp-includes/post.php:1685

Retrieves the post types currently registered on the site by matching them against an array of key value pairs. The $output argument decides whether you get back plain name strings or full WP_Post_Type objects, and $operator decides whether all, any, or none of the $args conditions must match. Because it reads the live $wp_post_types global, it only sees types registered before the call runs, so it is normally used on or after the init hook.

Gets a list of all registered post type objects.

Compatibility

WordPress
since 2.9.0
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$argsarray|stringoptional
An array of key => value arguments to match against the post type objects. Default empty array.Default: array()
$outputstringoptional
The type of output to return. Either 'names' or 'objects'. Default 'names'.Default: 'names'
$operatorstringoptional
The logical operation to perform. 'or' means only one element from the array needs to match; 'and' means all elements must match; 'not' means no elements may match. Default 'and'.Default: 'and'

Return value

string[]|WP_Post_Type[]
An array of post type names or objects.

Code examples

Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.

List the names of all public post types

A quick way to see which post types a theme or plugin can safely show in a public archive or search form.

$public_post_types = get_post_types( array( 'public' => true ) );

echo '<pre>' . esc_html( print_r( $public_post_types, true ) ) . '</pre>';

On a fresh install this includes 'post', 'page' and 'attachment' since all three are registered as public.

Get full post type objects to read their labels

Sometimes a name string is not enough and you need the labels or capabilities that only live on the object.

$post_type_objects = get_post_types( array( 'public' => true ), 'objects' );

foreach ( $post_type_objects as $post_type ) {
	printf(
		'<p>%s &rarr; %s</p>',
		esc_html( $post_type->name ),
		esc_html( $post_type->labels->name )
	);
}

Common problems and fixes · 3

Why does get_post_types() return an empty array or miss my custom post type?

The function only inspects the $wp_post_types global as it exists at the moment you call it. If your call runs before the post type is registered (register_post_type() typically runs on the init hook), it simply is not there yet.

Why am I getting an array of strings when I expected WP_Post_Type objects?

The default value of $output is 'names', which makes the function pass 'name' as the field to wp_filter_object_list() and return plain strings. Pass 'objects' explicitly if you need labels, capabilities, or other object properties.

Why does adding a second key to $args exclude a post type I expected to see?

The default $operator is 'and', so every key in $args must match a given post type object for it to be included. If you only need one of several conditions to be true, you have to switch the operator.

Alternatives and related functions

get_post_type_object
When you already know the exact post type name and just need its single WP_Post_Type object instead of filtering a whole list.
post_type_exists
When all you need is a true or false check for whether a given post type name is registered.
register_post_type
When the post type is not registered yet and needs to be added to $wp_post_types before get_post_types() can see it.

Performance profile

How much work a call to get_post_types() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Trivial

Touches nothing outside its own arguments.

Scaling
Constant

No loop in the body: the same number of instructions runs whatever you pass in.

Instructions
15–16

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 17.

Plugin surface
None

Nothing here hands control to plugin code.

Called by
50

50 places in core call this, so the cost is paid more often than your own code shows.

What one call costs · 1 distinct outcome

One number would be a lie: the work depends on which branch runs. These are every distinct cost get_post_types() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
always15–16wp_filter_object_list()

Across PHP versions

Compiles the same on PHP 7.4, 8.1, 8.2, 8.3, 8.4, 8.5 and 8.6-dev: 17 instructions, 15–16 executed per call, 1 branch. The work does not change between versions.

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Uses · 1

Used by · 50

Show all 50

Source code

function get_post_types( $args = array(), $output = 'names', $operator = 'and' ) {	global $wp_post_types; 	$field = ( 'names' === $output ) ? 'name' : false; 	return wp_filter_object_list( $wp_post_types, $args, $operator, $field );}

Changelog

Introduced in 2.9.0. Unchanged from 6.7.7 through 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

About this page

Parsed data
Generated from the wordpress-develop 7.1.0 tag, from src/wp-includes/post.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.