Skip to main content

mago_codex/metadata/
property_hook.rs

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