{"record":{"id":"2ca4ebab64c468bc","repo":"yiisoft/yii2","slug":"setting-read-only-property-class-name","errorCode":null,"errorMessage":"Setting read-only property: {class}::{name}","messagePattern":"Setting read-only property: (.+?)::(.+?)","errorType":"exception","errorClass":"InvalidCallException","httpStatus":null,"severity":"error","filePath":"framework/base/BaseObject.php","lineNumber":162,"sourceCode":"\n    /**\n     * Sets value of an object property.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `$object->property = $value;`.\n     * @param string $name the property name or the event name\n     * @param mixed $value the property value\n     * @throws UnknownPropertyException if the property is not defined\n     * @throws InvalidCallException if the property is read-only\n     * @see __get()\n     */\n    public function __set($name, $value)\n    {\n        $setter = 'set' . $name;\n        if (method_exists($this, $setter)) {\n            $this->$setter($value);\n        } elseif (method_exists($this, 'get' . $name)) {\n            throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);\n        } else {\n            throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);\n        }\n    }\n\n    /**\n     * Checks if a property is set, i.e. defined and not null.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `isset($object->property)`.\n     *\n     * Note that if the property is not defined, false will be returned.\n     * @param string $name the property name or the event name\n     * @return bool whether the named property is set (not null).\n     * @see https://www.php.net/manual/en/function.isset.php\n     */\n    public function __isset($name)\n    {","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/base/BaseObject.php#L144-L180","documentation":"BaseObject::__set() throws InvalidCallException('Setting read-only property: C::p') when the name has a getter but no setter and is not a public field - the property is computed or derived and immutable from outside. Yii deliberately blocks the write instead of silently ignoring it, so the failing assignment is in your code or your config.","triggerScenarios":"Assigning to a computed property ($n->fullName = 'A B' where getFullName() concatenates two stored fields); putting a read-only property key into a config array, because component configuration assigns every key through __set(); immutably-designed value objects used where mutable DTOs were expected.","commonSituations":"Config snippets copied from examples that target a different class version; value objects (money, date ranges) treated as writable; refactors that introduced a getter without removing old assignments.","solutions":["Remove the assignment and set the underlying writable properties instead","If mutation is legitimate, add a setter setX() alongside the existing getter","In configuration, delete the read-only key or map it to the writable property it derives from"],"exampleFix":"// before\nclass Name extends \\yii\\base\\BaseObject\n{\n    public $first;\n    public $last;\n    public function getFullName() { return $this->first . ' ' . $this->last; }\n}\n$n->fullName = 'Ada B'; // InvalidCallException\n\n// after\n$n->first = 'Ada';\n$n->last = 'B';","handlingStrategy":"type-guard","validationCode":"if ($object->canSetProperty($name)) {\n    $object->$name = $value;\n} else {\n    throw new \\LogicException(\"{$name} on \" . get_class($object) . ' is read-only');\n}","typeGuard":"function canWrite(\\yii\\base\\BaseObject $object, string $name): bool\n{\n    return $object->canSetProperty($name);\n}","tryCatchPattern":null,"preventionTips":["Annotate computed properties with @property-read so tooling blocks assignment","Change the underlying source properties instead of computed ones","For config arrays, validate keys against canSetProperty before assignment loops"],"tags":["oop","magic-methods","property-access","immutable"],"backgroundTag":"readonly-property-write","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}