<?php require __DIR__ . '/../vendor/autoload.php'; /** * :foo = {}; * :bar = true; */ class Foo { /** :yep = false; */ public $pants; /** :yep = true; */ public function setWhoopee() { } } /** * :foo = ["z"]; * :baz = "DING"; */ class Bar extends Foo { /** :yep = true; */ public $pants; public function setWhoopee() { } } $p = new \Nope\Parser(); dump($p->parseHierarchy(Bar::class));
protected function loadMeta($class) { $ref = new \ReflectionClass($class); if (!$this->parser) { $this->parser = new \Nope\Parser(); } $notes = $this->parser->parseHierarchy($ref, \ReflectionProperty::IS_PUBLIC, \ReflectionMethod::IS_PUBLIC); $classNotes = $notes->notes; if (!isset($classNotes[$this->annotationNamespace])) { return null; } $info = $classNotes[$this->annotationNamespace]; if ($info === true) { $info = []; } table: if (!isset($info['table'])) { $info['table'] = $this->getDefaultTable($class); } class_relations: if (isset($info['relations'])) { foreach ($info['relations'] as $relKey => &$relDef) { $type = $relDef['type']; unset($relDef['type']); array_unshift($relDef, $type); if (!is_array($relDef)) { throw new \Exception("Relation {$relKey} was not valid in class {$class}"); } $relDef['mode'] = 'class'; } unset($relDef); } class_indexes: if (isset($info['indexes'])) { // TODO: should be in Meta so the Arrays mapper can share it foreach ($info['indexes'] as $idxKey => &$idxDef) { if ($idxDef === true) { $idxDef = ['fields' => [$idxKey]]; } } unset($idxDef); } foreach (array('property' => $notes->properties, 'method' => $notes->methods) as $noteType => $noteBag) { foreach ($noteBag as $name => $itemNotes) { if (!isset($itemNotes[$this->annotationNamespace])) { goto next_note_bag; } $itemNotes = $itemNotes[$this->annotationNamespace]; validate: if ($diff = array_diff(array_keys($itemNotes), ['has', 'field', 'constructor'])) { throw new \UnexpectedValueException("Invalid keys found in :amiss field/method annotation: " . implode(', ', $diff)); } if (isset($itemNotes['field']) && isset($itemNotes['has'])) { throw new \UnexpectedValueException("Invalid class {$class}: relation and a field declared together on {$name}"); } field: if (isset($itemNotes['field'])) { $field = $itemNotes['field']; // NOTE: do not ensure $field['name'] is set here - it happens later in // one big hit. // FIXME: this should also happen in the Meta, but we need to guarantee we // are operating on an array to collect getters and setters, etc if ($field === true) { $field = []; } elseif (is_string($field)) { $field = ['name' => $field]; } elseif ($field === false) { // allows us to remove properties from consideration in child classes, i.e. :amiss = {"field": false}; goto next_note_bag; } elseif (!is_array($field)) { throw new \UnexpectedValueException(); } if ($noteType == 'method') { $key = $this->fillGetterSetter($name, $field, !!'readOnly'); } else { $key = $name; } $manualKey = isset($field['id']) ? $field['id'] : null; unset($field['id']); $info['fields'][$manualKey ?: $key] = $field; } field_relation: if (isset($itemNotes['has'])) { $relation = $itemNotes['has']; if (is_string($relation)) { $relation = ["type" => $relation]; } elseif ($relation === false) { goto next_note_bag; } elseif (!is_array($relation)) { throw new \UnexpectedValueException(); } if (!isset($relation['type'])) { throw new \UnexpectedValueException("Relation {$name} missing 'type'"); } $type = $relation['type']; unset($relation['type']); array_unshift($relation, $type); if ($noteType == 'method') { $key = $this->fillGetterSetter($name, $relation, !!'readOnly'); } else { $key = $name; } $key = isset($relation['id']) ? $relation['id'] : $key; unset($relation['id']); if (isset($info['relations'][$key])) { throw new \UnexpectedValueException("Duplicate relation {$name} on class {$class}"); } $info['relations'][$key] = $relation; } constructor: if ($noteType == 'method' && isset($itemNotes['constructor'])) { if (isset($info['constructor']) && $info['constructor']) { throw new \UnexpectedValueException("Constructor already declared: {$info['constructor']}"); } $info['constructor'] = $name; if ($itemNotes['constructor'] !== true) { if (!is_array($itemNotes['constructor'])) { throw new \UnexpectedValueException(); } if (isset($info['constructorArgs']) && $info['constructorArgs']) { throw new \UnexpectedValueException("Constructor args declared at class level and also on method {$name}."); } $info['constructorArgs'] = $itemNotes['constructor']; } } next_note_bag: } } if (isset($info['fields'])) { $info['fields'] = $this->resolveUnnamedFields($info['fields']); } return new \Amiss\Meta(ltrim($class, '\\'), $info); }