---
title: PHP filters
description: Every cartpops_ filter in CartPops V2 with its signature, accepted values, fallback rules and an example.
sidebar:
  label: PHP filters
  icon: filter
---

CartPops V2 has 15 public filters. Most of them validate what you return and fall back to a safe value, silently, when the value is not accepted. If a filter seems to have no effect, check the accepted values below first.

| Filter | Purpose | Edition |
| --- | --- | --- |
| [`cartpops_add_to_cart_trigger`](#cartpops_add_to_cart_trigger) | Decide whether adding to the cart opens the drawer. | Free and Pro |
| [`cartpops_checkout_button_url`](#cartpops_checkout_button_url) | Change the checkout button destination. | Free and Pro |
| [`cartpops_empty_cart_button_url`](#cartpops_empty_cart_button_url) | Change the Continue shopping destination in the empty cart. | Free and Pro |
| [`cartpops_cart_item_data`](#cartpops_cart_item_data) | Add text rows or plugin data to a cart line. | Free and Pro |
| [`cartpops_powered_by_link`](#cartpops_powered_by_link) | Change the Powered by link destination. | Free and Pro |
| [`cartpops_secondary_btn_classes`](#cartpops_secondary_btn_classes) | Add CSS classes to the secondary action button. | Pro |
| [`cartpops_pro_recommendation_button_presentation`](#cartpops_pro_recommendation_button_presentation) | Change the recommendation add button label and style. | Pro |
| [`cartpops_store_api_cart_extension_data`](#cartpops_store_api_cart_extension_data) and [`_schema`](#cartpops_store_api_cart_extension_schema) | Add data to the `cartpops` Store API namespace. | Free and Pro |
| [`cartpops_menu_launcher_attributes`](#cartpops_menu_launcher_attributes) | Change a menu launcher's attributes. | Free and Pro |
| [`cartpops_custom_css_style_dependencies`](#cartpops_custom_css_style_dependencies) | Control which stylesheets load before Custom CSS. | Free and Pro |
| [`cartpops_render_drawer`](#cartpops_render_drawer) | Stop CartPops adding the drawer to the footer. | Free and Pro |
| [`cartpops_rest_allowed_origins`](#cartpops_rest_allowed_origins) | Allow another origin to call the `cartpops/v1` routes. | Free and Pro |
| [`cartpops_rest_trusted_proxy_cidrs`](#cartpops_rest_trusted_proxy_cidrs) | Tell the rate limiter which proxies to trust. | Free and Pro |
| [`cartpops_elementor_widget_cart_is_hidden`](#cartpops_elementor_widget_cart_is_hidden) | Hide the CartPops Elementor widget. | Pro |

Filters that are not listed here are internal. The `cartpops_secondary_btn_attributes` filter from V1 is not supported in V2.

:::warning[Exceptions]
These filters catch an exception thrown by your callback and fall back to the safe value: `cartpops_cart_item_data`, both `cartpops_store_api_cart_extension_*` filters, `cartpops_pro_recommendation_button_presentation` and `cartpops_secondary_btn_classes`. The other filters do not. An exception in one of those breaks the render or the request, so keep their callbacks simple.
:::

## `cartpops_add_to_cart_trigger`

```text
apply_filters( 'cartpops_add_to_cart_trigger', string $trigger ): string
```

Receives the **Open drawer on** setting (Advanced > General) when the drawer renders. Return `add_to_cart` to open the drawer after a successful add to cart, or `launcher` to open it only when the shopper uses a launcher.

| Prop | Type | Default | Description |
| - | - | - | - |
| `add_to_cart?` | `string` | - | A successful add to cart opens the drawer. Legacy aliases both, drawer, popup and bar are converted to this. |
| `launcher?` | `string` | - | The drawer opens only from a launcher or your own code. Legacy aliases manual and none are converted to this. |

Any other result falls back to `launcher`: non-strings, values longer than 32 characters, and values that differ in case or whitespace (`Add_To_Cart`, ` add_to_cart`). The filter runs when the drawer is rendered, so a full-page cache keeps whatever value the cached page was built with.

```php functions.php
// Keep the drawer closed on the cart and checkout pages.
add_filter( 'cartpops_add_to_cart_trigger', static function ( $trigger ) {
	return ( is_cart() || is_checkout() ) ? 'launcher' : $trigger;
} );
```

## `cartpops_checkout_button_url`

```text
apply_filters( 'cartpops_checkout_button_url', string $url ): string
```

Receives the WooCommerce checkout URL. The result becomes the checkout button link, and it is kept when the drawer updates in the browser.

```php functions.php
add_filter( 'cartpops_checkout_button_url', static function ( string $url ): string {
	return add_query_arg( 'source', 'drawer', $url );
} );
```

The same rules apply to both URL filters. Return a plain URL, not HTML. CartPops accepts a result that is:

- an absolute `http` or `https` URL with a host, or
- a root-relative site URL that starts with a single slash, such as `/shop/`.

CartPops rejects and falls back to the original WooCommerce URL for a bare relative path such as `shop`, an empty string, a non-string, a protocol-relative URL (`//example.com`), a URL with a username or password, a URL with spaces, control characters or a backslash, any other scheme (`javascript:`, `mailto:`), and a URL longer than 8,192 characters.

## `cartpops_empty_cart_button_url`

```text
apply_filters( 'cartpops_empty_cart_button_url', string $url ): string
```

Receives the WooCommerce shop page URL. The result becomes the **Continue shopping** link in the empty cart. Opening the link closes the drawer and then navigates. The same URL rules as the checkout filter apply.

```php functions.php
add_filter( 'cartpops_empty_cart_button_url', static function (): string {
	return '/collections/best-sellers/';
} );
```

## `cartpops_cart_item_data`

```text
apply_filters( 'cartpops_cart_item_data', array $item, array $cart_item, string $cart_key ): array
```

Adjust how one cart line is presented in the drawer. This is the V2 replacement for V1's per-item HTML and class hooks. Add the callback with three accepted arguments: `add_filter( 'cartpops_cart_item_data', $callback, 10, 3 )`.

| Prop | Type | Default | Description |
| - | - | - | - |
| `$item?` | `array` | - | CartPops' presentation row for this line. Return it with your changes. |
| `$cart_item?` | `array` | - | The raw WooCommerce cart item, including your own custom keys. |
| `$cart_key?` | `string` | - | The exact WooCommerce cart item key. |

The filter runs every time CartPops rebuilds its cart state in PHP: the first render, add-to-cart fragments, CartPops cart reads and changes, and WooCommerce Store API cart responses. The result travels to the browser, so it stays in place when the shopper changes quantities or removes lines. Do not keep per-request state in the callback.

### Fields you can change

CartPops accepts only these five fields from your return value. It validates each one on its own. If one field is invalid, only that field falls back.

| Prop | Type | Default | Description |
| - | - | - | - |
| `name?` | `string` | - | Replaces the product name. Text only, tags are stripped. Up to 500 bytes. Cannot be empty. |
| `short_description?` | `string` | - | Replaces the short description. Text only. Up to 2,000 bytes. |
| `variationSummary?` | `string` | - | Replaces the variation summary. Text only. Up to 1,000 bytes. |
| `extraLines?` | `array<array{label: string, value: string}>` | - | Up to 20 rows shown under the product name. Each row has exactly the keys label and value. A label is required and up to 160 bytes. A value is up to 1,000 bytes. Tags and control characters are stripped. If any row is invalid the whole field is rejected. |
| `customPresentation?` | `array<string, mixed>` | - | Plain data for your own client code. CartPops never renders it. Keys must start with a letter and use only letters, numbers, underscore, hyphen or dot, up to 64 bytes. Up to 16 entries per level, 4 levels deep, 64 values and 8,192 bytes of JSON. Strings are plain text up to 1,024 bytes. |

Use a unique key for your plugin inside `customPresentation`, such as `my_plugin`, so plugins do not overwrite each other.

`extraLines` already holds the rows that other plugins add with WooCommerce's own `woocommerce_get_item_data` filter. Append to it to keep those rows. Replace it to drop them. For a plain label and value row, `woocommerce_get_item_data` also works and is picked up the same way.

### Fields you receive but cannot change

CartPops restores every other field, including any that a future version adds. The row you receive includes `key`, `id`, `product_id`, `permalink`, `quantity`, `isOnSale`, `images`, `prices`, `formattedPrice`, `formattedRegularPrice` and the fields that control quantity and removal. A callback cannot change the cart key used by remove and quantity requests, prices, product identity, whether a line is locked, or link and image URLs. Returning HTML, directives, classes or scripts is not supported.

```php functions.php
add_filter(
	'cartpops_cart_item_data',
	static function ( array $item, array $cart_item, string $cart_key ): array {
		if ( empty( $cart_item['my_engraving'] ) ) {
			return $item;
		}

		$item['extraLines'][] = array(
			'label' => __( 'Engraving', 'my-plugin' ),
			'value' => (string) $cart_item['my_engraving'],
		);
		$item['customPresentation']['my_plugin'] = array(
			'cartItemKey'  => $cart_key,
			'personalized' => true,
		);

		return $item;
	},
	10,
	3
);
```

Over the Store API, the presentation of up to 100 cart lines and 1 MiB of data per response is carried. If a cart exceeds that, CartPops drops the optional presentation for that response and keeps the cart usable. See [Store API and REST](/developers/store-api-and-rest) for where the data appears.

## `cartpops_powered_by_link`

```text
apply_filters( 'cartpops_powered_by_link', string $url ): string
```

Receives the Freemius partner URL for the **Powered by CartPops** link, which has the form `https://r.freemius.com/7061/{code}/`. It runs only when **Show the CartPops partner link** is on and a valid partner code is saved (Advanced settings). Otherwise the link is not shown and the filter does not run.

Return a complete `http` or `https` URL with a host. CartPops falls back to the original URL for a non-string, a URL with credentials, spaces, control characters or a backslash, an unsupported scheme, or a URL longer than 2,048 characters. Root-relative URLs are not accepted here.

```php functions.php
add_filter( 'cartpops_powered_by_link', static function (): string {
	return 'https://example.com/go/cartpops';
} );
```

## `cartpops_secondary_btn_classes`

```text
apply_filters( 'cartpops_secondary_btn_classes', array $classes, string $mode ): array
```

:::note[Pro feature]
Called only on a site with an active CartPops Pro license, and only when a secondary action is configured on the Drawer screen.
:::

Receives the list of CSS classes CartPops puts on the secondary action button, and the action mode: `continue_shopping`, `view_cart` or `custom_url`. You can add classes. You cannot remove CartPops' own classes, and you cannot change the label, destination, element type or behavior.

The result must be a list of at most 16 strings. Each class must be one CSS identifier that starts with a letter or underscore and has at most 64 letters, digits, underscores or hyphens. Duplicates are removed. If anything is wrong, such as a bad token, a non-list or too many entries, CartPops discards your whole result and uses its own classes.

```php functions.php
add_filter( 'cartpops_secondary_btn_classes', static function ( array $classes, string $mode ): array {
	$classes[] = 'my-secondary-button';
	$classes[] = 'my-secondary-button--' . str_replace( '_', '-', $mode );
	return $classes;
}, 10, 2 );
```

## `cartpops_pro_recommendation_button_presentation`

```text
apply_filters( 'cartpops_pro_recommendation_button_presentation', array $presentation ): array
```

:::note[Pro feature]
Called only on a site with an active CartPops Pro license.
:::

Receives the recommendation add button presentation, and runs once while the Pro drawer renders.

| Prop | Type | Default | Description |
| - | - | - | - |
| `mode?` | `"icon" \| "text" \| "text_icon"` | - | What the button shows. |
| `text?` | `string` | - | The label for the text modes. Tags are stripped and spaces are collapsed. Text longer than 80 characters is cut to 80. Empty text, control characters, directional characters and invalid UTF-8 are rejected. |

If the result is not an array, has an unknown mode or has unusable text, CartPops uses the hard defaults, `icon` and `Add`. It does not use the value saved in the settings. The filter changes only the look of the button. It cannot change which products are recommended or what the button does. Variable products keep their **Select options** link.

```php functions.php
add_filter( 'cartpops_pro_recommendation_button_presentation', static function ( array $presentation ): array {
	$presentation['mode'] = 'text_icon';
	$presentation['text'] = __( 'Add this item', 'my-plugin' );
	return $presentation;
} );
```

## `cartpops_store_api_cart_extension_data`

```text
apply_filters( 'cartpops_store_api_cart_extension_data', array $data ): array
```

Adds data to the `cartpops` namespace of the WooCommerce Store API cart response. It runs on every Store API cart response, including WooCommerce's own add to cart, so keep the callback fast. Return the complete array, with your keys added.

```php functions.php
add_filter( 'cartpops_store_api_cart_extension_data', static function ( array $data ): array {
	$data['my_plugin'] = array(
		'gift_wrap_available' => true,
	);
	return $data;
} );
```

The result is accepted only if the whole array is bounded JSON data. Allowed values are null, booleans, integers, finite numbers, valid UTF-8 strings, lists and maps with string keys. Not allowed: objects, resources, recursive arrays and non-finite numbers. The limits are 8 levels deep, 512 values, 128 entries per list or map, 128 bytes per key, 8,192 bytes per string and 65,536 bytes of JSON. The top level must be a map, not a list.

If any rule is broken, CartPops discards the whole result and uses its own baseline. CartPops always restores its own `coupons`, `item_presentations` and `optional_locked_keys` keys after your filter runs, so you cannot change or remove them. Avoid the keys `drawer_config` and `launcher` as well, which CartPops also uses.

## `cartpops_store_api_cart_extension_schema`

```text
apply_filters( 'cartpops_store_api_cart_extension_schema', array $schema ): array
```

The JSON schema for the data above. Add a schema entry for each key you add to the data. The same bounds and fallback rules apply, and CartPops restores its own schema for the three reserved keys.

```php functions.php
add_filter( 'cartpops_store_api_cart_extension_schema', static function ( array $schema ): array {
	$schema['my_plugin'] = array(
		'description' => 'My plugin cart data.',
		'type'        => 'object',
		'readonly'    => true,
	);
	return $schema;
} );
```

## `cartpops_menu_launcher_attributes`

```text
apply_filters( 'cartpops_menu_launcher_attributes', array $attributes, object $item, string $item_output ): array
```

Runs for each classic navigation menu item that has the `cpops-cart-menu-item` CSS class, just before CartPops replaces the item with an inline launcher. It does not run for other menu items or for the Navigation block.

| Prop | Type | Default | Description |
| - | - | - | - |
| `$attributes?` | `array` | - | The launcher attributes built from the Launcher > Navigation menu launcher (legacy) settings. Listed below. |
| `$item?` | `object` | - | The WordPress menu item. Use its classes or title to target a specific item. |
| `$item_output?` | `string` | - | The original markup of the menu item, before replacement. |

| Prop | Type | Default | Description |
| - | - | - | - |
| `icon?` | `"cart" \| "bag" \| "basket"` | - | The icon. V1 cpops-icon-* names map to cart or bag. Any other value falls back to the saved setting. |
| `indicator?` | `"none" \| "bubble" \| "plain"` | - | How the item count is shown. |
| `showCount?` | `bool` | - | False forces indicator to none. An indicator of none forces this to false. |
| `showTotal?` | `bool` | - | Show the cart total next to the icon. |
| `hideEmpty?` | `bool` | - | Hide the whole launcher while the cart is empty. |
| `hideIndicatorEmpty?` | `bool` | - | Hide only the count while the cart is empty. |
| `presentation?` | `"inline"` | - | Always reset to inline. You cannot make a menu launcher floating. |

Booleans accept `true`, `false`, `1`, `0`, `"1"`, `"0"` and the strings `true`, `false`, `yes`, `no`, `on` and `off`. Any value that does not validate falls back to the saved menu launcher setting. A result that is not an array uses all saved settings.

```php functions.php
add_filter( 'cartpops_menu_launcher_attributes', static function ( array $attributes, object $item ): array {
	if ( in_array( 'my-header-cart', (array) $item->classes, true ) ) {
		$attributes['icon']      = 'basket';
		$attributes['showTotal'] = true;
	}
	return $attributes;
}, 10, 2 );
```

## `cartpops_custom_css_style_dependencies`

```text
apply_filters( 'cartpops_custom_css_style_dependencies', string[] $handles ): string[]
```

Your Custom CSS (Advanced > General > Custom CSS) is added as an inline stylesheet that depends on the CartPops drawer stylesheet. This filter lists the style handles that the Custom CSS depends on, which controls what loads before it. It runs only when Custom CSS is not empty and CartPops is enabled.

The default is `array( 'cartpops-cart-drawer-style' )`. CartPops pins that handle first, so you cannot remove it. In CartPops Pro, the Pro plugin also uses this filter to add its own handle, so append to the array instead of replacing it.

CartPops ignores any entry that is not a string, is longer than 100 characters, does not match `a-z`, `0-9`, `.`, `_` and `-` (starting with a letter or digit), or is already in the list. Anything that is not an array leaves only the shared handle.

:::warning
Only add handles that are registered with WordPress. WordPress does not print a stylesheet whose dependency is not registered, so a wrong handle can stop your Custom CSS from loading at all, with no error.
:::

```php functions.php
// Load Custom CSS after the launcher stylesheet and a theme stylesheet.
add_filter( 'cartpops_custom_css_style_dependencies', static function ( $handles ) {
	$handles   = is_array( $handles ) ? $handles : array();
	$handles[] = 'cartpops-cart-launcher-style';
	$handles[] = 'my-theme-style';
	return $handles;
} );
```

## `cartpops_render_drawer`

```text
apply_filters( 'cartpops_render_drawer', bool $render ): bool
```

Decides whether CartPops adds its drawer to `wp_footer`. The default is `true`. Any falsy result turns the automatic drawer off.

The filter only affects the automatic footer drawer. It is skipped when CartPops is switched off, and when a Cart Drawer block is already printed by a template or page, because that block is used instead. It does not remove the launcher. If you turn the drawer off, also turn off **Show floating cart launcher** on the Launcher screen, or its button will do nothing.

```php functions.php
// No automatic drawer on the checkout page.
add_filter( 'cartpops_render_drawer', static function ( $render ) {
	return is_checkout() ? false : $render;
} );
```

## `cartpops_rest_allowed_origins`

```text
apply_filters( 'cartpops_rest_allowed_origins', string[] $urls ): string[]
```

Controls which origins can call the `cartpops/v1` REST routes from a browser. The default list holds your site's home and site URLs. Add an origin when a headless front end or a second domain on a different host needs to call the routes.

- Each entry must be a full `http` or `https` URL. CartPops reduces it to scheme, host and port and compares it exactly. `https://www.example.com` and `https://example.com` are different origins.
- The check applies to requests that send an `Origin` or `Referer` header, which is every browser request. A request with neither header, such as one from a server, passes this check. The other protections still apply.
- If the origin and referrer both exist, they must agree.
- A rejected request gets HTTP 403 with the code `cartpops_forbidden_origin`.
- Matching origins also get CORS headers that allow credentials.

:::warning[Append, do not replace]
Always add to the list you receive. If your callback returns something that is not an array, or the list has more than 16 entries, CartPops treats the allowlist as empty and rejects every browser request. Only add origins you control, because an allowed origin can use the shopper's cart session.
:::

This filter applies only to `cartpops/v1`. It does not change WooCommerce Store API requests.

```php functions.php
add_filter( 'cartpops_rest_allowed_origins', static function ( $urls ) {
	$urls   = is_array( $urls ) ? $urls : array();
	$urls[] = 'https://shop.example.com';
	return $urls;
} );
```

## `cartpops_rest_trusted_proxy_cidrs`

```text
apply_filters( 'cartpops_rest_trusted_proxy_cidrs', string[] $cidrs ): string[]
```

CartPops rate limits its write routes per client IP address and per visitor. By default it uses the connection's own address. Behind a CDN or reverse proxy, that address is the proxy's, so every visitor shares one limit and shoppers can start to see HTTP 429 responses. Return the networks of the proxies you trust, and CartPops will then read the visitor's address from the `X-Forwarded-For` header.

- Each entry is `address/prefix` in CIDR form, for IPv4 or IPv6. The prefix must be at least 1 and no larger than the address size (32 or 128). Use `203.0.113.7/32` for a single address, not a bare IP.
- At most 32 entries.
- Only `X-Forwarded-For` is read. Other headers such as `CF-Connecting-IP` are ignored.
- When the connection comes from a trusted proxy, CartPops reads the header and uses the right-most address that is not itself a trusted proxy. The header must be present, at most 1,024 bytes, hold at most 10 addresses, and every entry must be a valid IP.
- Anything outside your list is treated as an ordinary visitor and its `X-Forwarded-For` header is ignored.

:::danger[A bad value breaks rate-limited routes]
If any entry is malformed, or the list is not an array or has more than 32 entries, every rate-limited route returns HTTP 503 with the code `cartpops_rate_limit_unavailable`. The same happens when a trusted proxy sends a missing or invalid `X-Forwarded-For` header. Read-only routes such as `GET /cart` are not rate limited and keep working. List only networks you control or your CDN's published ranges, because CartPops believes the header from them.
:::

```php functions.php
add_filter( 'cartpops_rest_trusted_proxy_cidrs', static function (): array {
	return array(
		'203.0.113.0/24',
		'2001:db8::/32',
	);
} );
```

## `cartpops_elementor_widget_cart_is_hidden`

```text
apply_filters( 'cartpops_elementor_widget_cart_is_hidden', bool $hidden ): bool
```

:::note[Pro feature]
Only used by the CartPops Cart widget for Elementor, which is part of CartPops Pro.
:::

Receives `false`. Return `true` to make the widget print nothing on the current request. The filter takes no widget or page argument, so check the page yourself.

```php functions.php
// Hide the Elementor cart widget on the checkout page.
add_filter( 'cartpops_elementor_widget_cart_is_hidden', static function ( $hidden ) {
	return is_checkout() ? true : $hidden;
} );
```

:::note[Coming from V1?]
The per-item HTML hooks, totals filters, class filters and `cartpops_secondary_btn_attributes` are gone. The two URL filters, the trigger filter and the powered by filter kept their names but now validate their results. See [Upgrading to V2: for developers](/upgrading-to-v2/developers).
:::
