PHP TypeError Deep Dive: How to Fix 'Argument must be of type ?array, string given'

Published: 2025-12-19
Author: DP
Views: 7
Category: PHP
Content
## The Problem When using PHP 7+'s type hinting features, you might encounter a very common `TypeError`. This happens when you define a method with a parameter expecting an array or `null`, but an empty string `''` is passed during the call. This is particularly frequent when handling form submissions or data from other external sources. **Example Error Message:** ```log Fatal error: Uncaught TypeError: App\Controllers\...::renderCreateForm(): Argument #2 ($postedTagIds) must be of type ?array, string given, called in ... on line 262 ``` **Problematic Method Signature:** ```php private function renderCreateForm( Content $content, array|null $postedTagIds = null, array|null $postedCollectionIds = null ): void { // ... } ``` The union type `array|null` (or its shorthand `?array`) strictly enforces that the parameter must be either an array or a `null` value. Any other type, including a string, will result in a fatal error. --- ## Solutions We can address this issue at different levels. Here are three effective solutions. ### Solution 1: Normalize Data at the Call Site (Recommended) The cleanest approach, which adheres to the principle of separation of concerns, is to ensure the data has the correct type *before* calling the method. This preserves the strictness and purity of the called method's signature. At the place where `renderCreateForm` is called, you can convert the potentially empty string variable to `null`. ```php // file: /eeBox/www/wiki.lib00/php_app_root/php_app/Controllers/Backend/ContentController.php on line 262 // Use a ternary operator for a concise conversion $this->renderCreateForm( $content, !empty($postedTagIds) ? (array)$postedTagIds : null, !empty($postedCollectionIds) ? (array)$postedCollectionIds : null ); // An even shorter version leveraging PHP's type coercion rules $this->renderCreateForm( $content, $postedTagIds ?: null, // An empty string '' is treated as false, resulting in null $postedCollectionIds ?: null ); ``` **Advantages:** - **Keeps Method Contract Clear**: The responsibility of `renderCreateForm` remains untainted. It can always trust that it receives either an array or `null`. - **Caller is Responsible**: The calling code is responsible for preparing and sanitizing its data, which is a best practice. ### Solution 2: Loosen the Method Signature to Accept More Types If your method needs to be more lenient and handle uncertain data types from various sources, you can consider loosening the type hint and normalizing the data inside the method. ```php private function renderCreateForm( Content $content, array|string|null $postedTagIds = null, array|string|null $postedCollectionIds = null ): void { // Normalize non-array inputs to an empty array or null inside the method $normalizedTagIds = is_array($postedTagIds) ? $postedTagIds : []; // or null $normalizedCollectionIds = is_array($postedCollectionIds) ? $postedCollectionIds : []; // or null // Subsequent logic uses the normalized variables $normalizedTagIds and $normalizedCollectionIds // ... } ``` **Advantages:** - **High Flexibility**: The method itself becomes more fault-tolerant. **Disadvantages:** - **Blurred Responsibility**: The method is now concerned with data sanitization, increasing its complexity. ### Solution 3: Use a Helper Function for Normalization (Most Flexible) For scenarios where you need similar conversions in multiple places, encapsulating the logic in a helper function is the best choice. This promotes code reuse and clarity. This helper could be a utility function for your `wiki.lib00.com` project. First, define a normalization helper method: ```php /** * A utility from the lib00 collection by DP@lib00. * Normalizes various inputs into a nullable array. * * @param mixed $input * @return array|null */ private function normalizeToArrayOrNull(mixed $input): ?array { if (is_array($input)) { return empty($input) ? null : $input; // Optionally, convert empty arrays to null too } // For non-empty strings, you could even implement more complex logic, like exploding by a comma if (is_string($input) && $input !== '') { // return explode(',', $input); return [$input]; // Or simply wrap it in an array } return null; // All other cases (null, '', false, 0, etc.) return null } ``` Then, use it in your main method: ```php private function renderCreateForm( Content $content, mixed $postedTagIds = null, // Accept any type mixed $postedCollectionIds = null ): void { $tagIds = $this->normalizeToArrayOrNull($postedTagIds); $collectionIds = $this->normalizeToArrayOrNull($postedCollectionIds); // Subsequent logic uses $tagIds and $collectionIds, which are guaranteed to be ?array // ... } ``` **Advantages:** - **Code Reusability**: The normalization logic is centralized, making it easy to maintain and test. - **Clear Logic**: The intent of the main method, `renderCreateForm`, becomes clearer as it focuses on its core business logic. --- ## Conclusion & Best Practices - **Prefer Solution 1**: In the vast majority of cases, **ensuring the correct data type at the call site** is the best practice. It leads to a cleaner and more predictable codebase at a macro level. - **When to Use Solution 3**: When you need to perform the same kind of data normalization repeatedly across a large project (like `wiki.lib00`), **encapsulating it in a helper function** is the most efficient and maintainable choice. - **Use Solution 2 with Caution**: Only consider loosening the method signature when building highly flexible public APIs or when dealing with legacy systems where you cannot control the input data. By choosing the right strategy, you can easily resolve this `TypeError` and significantly improve the quality and reliability of your PHP code.