{"record":{"id":"792369022c9d4fd7","repo":"yiisoft/yii2","slug":"unsetting-read-only-property-class-name","errorCode":null,"errorMessage":"Unsetting read-only property: {class}::{name}","messagePattern":"Unsetting read-only property: (.+?)::(.+?)","errorType":"exception","errorClass":"InvalidCallException","httpStatus":null,"severity":"error","filePath":"framework/base/BaseObject.php","lineNumber":207,"sourceCode":"    /**\n     * Sets an object property to null.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `unset($object->property)`.\n     *\n     * Note that if the property is not defined, this method will do nothing.\n     * If the property is read-only, it will throw an exception.\n     * @param string $name the property name\n     * @throws InvalidCallException if the property is read only.\n     * @see https://www.php.net/manual/en/function.unset.php\n     */\n    public function __unset($name)\n    {\n        $setter = 'set' . $name;\n        if (method_exists($this, $setter)) {\n            $this->$setter(null);\n        } elseif (method_exists($this, 'get' . $name)) {\n            throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '::' . $name);\n        }\n    }\n\n    /**\n     * Calls the named method which is not a class method.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when an unknown method is being invoked.\n     * @param string $name the method name\n     * @param array $params method parameters\n     * @throws UnknownMethodException when calling unknown method\n     * @return mixed the method return value\n     */\n    public function __call($name, $params)\n    {\n        throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . \"::$name()\");\n    }\n","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/base/BaseObject.php#L189-L225","documentation":"unset() on an inaccessible property triggers BaseObject::__unset(): if a setter exists it is called with null; if only a getter exists the property is read-only and InvalidCallException('Unsetting read-only property: C::p') is thrown. Note that for completely unknown names __unset() does nothing silently - only the read-only case throws, so this error always means a getter-only property met an unset().","triggerScenarios":"unset($report->generatedAt) where only getGeneratedAt() exists; generic cleanup code that unsets fields before caching or serializing an object; array-hygiene routines (where unset is always safe) reused on objects with computed getters.","commonSituations":"Serialization slimming that strips computed getters before var_export/json_encode; refactors from array payloads to objects that kept the old unset() calls.","solutions":["Call the class's own clearing method, or clear via a setter instead of unset()","Add a setter if setting null is a meaningful operation for the class","Skip the unset when !$object->canSetProperty($name)"],"exampleFix":"// before\nunset($report->generatedAt); // getter only => InvalidCallException\n\n// after\n$report->clearGeneratedAt(); // explicit class method, or add setGeneratedAt(null) support","handlingStrategy":"validation","validationCode":"if ($object->canSetProperty($name)) {\n    unset($object->$name); // routes through the setter with null\n}\n// read-only properties are left untouched - no exception","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not port array-cleanup unset() loops onto objects verbatim","Expose explicit clear/reset methods on classes whose state must be clearable","Guard unsets with canSetProperty() in generic serialization helpers"],"tags":["oop","magic-methods","unset","property-access"],"backgroundTag":"readonly-property-unset","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}