mago_codex/metadata/
property_hook.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_atom::Atom;
5use mago_reporting::Issue;
6use mago_span::Span;
7
8use crate::metadata::attribute::AttributeMetadata;
9use crate::metadata::flags::MetadataFlags;
10use crate::metadata::parameter::FunctionLikeParameterMetadata;
11use crate::metadata::ttype::TypeMetadata;
12
13/// Metadata for a property hook (get or set).
14///
15/// PHP 8.4 introduced property hooks, which allow defining custom get/set behavior
16/// for properties. This struct stores the metadata for a single hook.
17#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
18pub struct PropertyHookMetadata {
19    /// The hook name ("get" or "set").
20    pub name: Atom,
21
22    /// Span of the hook declaration.
23    pub span: Span,
24
25    /// Hook modifiers (final, etc.).
26    pub flags: MetadataFlags,
27
28    /// For set hooks: the parameter (explicit or implicit $value).
29    /// None for get hooks.
30    pub parameter: Option<FunctionLikeParameterMetadata>,
31
32    /// Whether the hook returns by reference (&get).
33    pub returns_by_ref: bool,
34
35    /// Whether this is an abstract hook (no body, just semicolon).
36    pub is_abstract: bool,
37
38    /// Attributes on the hook.
39    pub attributes: Vec<AttributeMetadata>,
40
41    /// Return type from @return docblock (for get hooks).
42    pub return_type_metadata: Option<TypeMetadata>,
43
44    /// Whether this hook has a docblock comment.
45    pub has_docblock: bool,
46
47    /// Issues from parsing the docblock.
48    pub issues: Vec<Issue>,
49}
50
51impl PropertyHookMetadata {
52    /// Creates a new `PropertyHookMetadata` with the given name and span.
53    #[inline]
54    #[must_use]
55    pub fn new(name: Atom, span: Span) -> Self {
56        Self {
57            name,
58            span,
59            flags: MetadataFlags::empty(),
60            parameter: None,
61            returns_by_ref: false,
62            is_abstract: false,
63            attributes: Vec::new(),
64            return_type_metadata: None,
65            has_docblock: false,
66            issues: Vec::new(),
67        }
68    }
69
70    /// Returns whether this is a get hook.
71    #[inline]
72    #[must_use]
73    pub fn is_get(&self) -> bool {
74        self.name.as_str() == "get"
75    }
76
77    /// Returns whether this is a set hook.
78    #[inline]
79    #[must_use]
80    pub fn is_set(&self) -> bool {
81        self.name.as_str() == "set"
82    }
83
84    /// Sets the flags for this hook.
85    #[inline]
86    #[must_use]
87    pub fn with_flags(mut self, flags: MetadataFlags) -> Self {
88        self.flags = flags;
89        self
90    }
91
92    /// Sets the parameter for this hook (for set hooks).
93    #[inline]
94    #[must_use]
95    pub fn with_parameter(mut self, parameter: Option<FunctionLikeParameterMetadata>) -> Self {
96        self.parameter = parameter;
97        self
98    }
99
100    /// Sets whether the hook returns by reference.
101    #[inline]
102    #[must_use]
103    pub fn with_returns_by_ref(mut self, returns_by_ref: bool) -> Self {
104        self.returns_by_ref = returns_by_ref;
105        self
106    }
107
108    /// Sets whether this is an abstract hook.
109    #[inline]
110    #[must_use]
111    pub fn with_is_abstract(mut self, is_abstract: bool) -> Self {
112        self.is_abstract = is_abstract;
113        self
114    }
115
116    /// Sets the attributes for this hook.
117    #[inline]
118    #[must_use]
119    pub fn with_attributes(mut self, attributes: Vec<AttributeMetadata>) -> Self {
120        self.attributes = attributes;
121        self
122    }
123
124    /// Sets the return type metadata from docblock (for get hooks).
125    #[inline]
126    #[must_use]
127    pub fn with_return_type_metadata(mut self, return_type_metadata: Option<TypeMetadata>) -> Self {
128        self.return_type_metadata = return_type_metadata;
129        self
130    }
131
132    /// Sets whether this hook has a docblock.
133    #[inline]
134    #[must_use]
135    pub fn with_has_docblock(mut self, has_docblock: bool) -> Self {
136        self.has_docblock = has_docblock;
137        self
138    }
139
140    /// Sets the issues from parsing the docblock.
141    #[inline]
142    #[must_use]
143    pub fn with_issues(mut self, issues: Vec<Issue>) -> Self {
144        self.issues = issues;
145        self
146    }
147
148    /// Takes the issues, leaving an empty vector.
149    #[inline]
150    pub fn take_issues(&mut self) -> Vec<Issue> {
151        std::mem::take(&mut self.issues)
152    }
153}