|
1 | 1 | <!-- |
2 | 2 | { |
3 | | - "title": "Static scope in components", |
| 3 | + "title": "Static Scope in Components", |
4 | 4 | "id": "static-scope-in-components", |
5 | 5 | "categories": [ |
6 | 6 | "component", |
7 | 7 | "scopes", |
8 | 8 | "static" |
9 | 9 | ], |
10 | | - "description": "Static scope in components is needed to create an instance of cfc and call its method.", |
| 10 | + "description": "Understanding the static scope in Lucee components and how it can be used for shared data and functions.", |
11 | 11 | "keywords": [ |
12 | 12 | "Static scope", |
13 | 13 | "Components", |
|
19 | 19 | } |
20 | 20 | --> |
21 | 21 |
|
22 | | -# Static scope in components |
| 22 | +# Static Scope in Components |
23 | 23 |
|
24 | | -Static scope in components is needed to create an instance of cfc and call its method. It is used to avoid creating an instance each time you use the cfc. |
| 24 | +## Understanding Static Scope |
25 | 25 |
|
26 | | -You can create an object in the Application init() function, and make it at application scope, so you can directly call the methods. |
| 26 | +The **static scope** in Lucee components allows variables and functions to be shared across all instances of a component. This avoids the need to create a new instance every time a function is called, improving efficiency and consistency. |
27 | 27 |
|
28 | | -We explain this methodology with a simple example below: |
| 28 | +Static scope was introduced in Lucee 5.0 and provides a way to store shared state at the component level rather than per instance. |
29 | 29 |
|
30 | | -## Example 1 |
| 30 | +## Defining Static Variables |
31 | 31 |
|
32 | | -```luceescript |
33 | | -// index.cfm |
34 | | -directory sort="name" action="list" directory=getDirectoryFromPath(getCurrentTemplatePath()) filter="example*.cfm" name="dir"; |
35 | | -loop query=dir { |
36 | | - echo('<a href="#dir.name#">#dir.name#</a><br>'); |
37 | | -} |
38 | | -``` |
39 | | - |
40 | | -1. Create a constructor of the component. It is the instance of the current path and also create new function hey(). |
41 | | - |
42 | | -```luceescript |
43 | | -// Example0.cfc |
44 | | -Component { |
45 | | - public function init() { |
46 | | - dump("create an instance of " & listLast(getCurrentTemplatePath(),'\/')); |
47 | | - } |
48 | | - public function hey() { |
49 | | - dump("Salve!"); |
50 | | - } |
51 | | -} |
52 | | -``` |
| 32 | +A static variable is shared among all instances of a component and retains its value across multiple calls: |
53 | 33 |
|
54 | | -2. Next, we instantiate the component four times, and then call the hey() function. Run this example00.cfm page in the browser. It shows five dumps. Four dumps coming from inside of the constructor and the fifth dump is from hey(). Note that the init() function is private, so you cannot load it from outside the component. Therefore, you have no access to the message within init() from the cfscript in the example below. |
55 | | - |
56 | | -```luceescript |
57 | | -// example0.cfm |
58 | | -new Example0(); |
59 | | -new Example0(); |
60 | | -new Example0(); |
61 | | -cfc = new Example0(); |
62 | | -cfc.hey(); |
63 | 34 | ``` |
64 | | - |
65 | | -## Example 2 |
66 | | - |
67 | | -As our code gets more complicated, we need to make some additions to it. |
68 | | - |
69 | | -- One option is to create the Component in the application scope or server scope, or to use the function GetComponentMetaData to store components in a more persistent manner. |
70 | | - |
71 | | -The static scope for components was introduced in Lucee 5.0. This is a scope that is shared with all instances of the same component. |
72 | | - |
73 | | -Here is an example showing the use of static scope: |
74 | | - |
75 | | -```luceescript |
76 | | -// Example1.cfc |
77 | | -Component { |
| 35 | +component { |
78 | 36 | static var counter = 0; |
| 37 | + |
79 | 38 | public function init() { |
80 | 39 | static.counter++; |
81 | | - dump("create an instance of " & listLast(getCurrentTemplatePath(),'\/') & " " & static.counter); |
| 40 | + dump("Instance created: " & static.counter); |
82 | 41 | } |
| 42 | + |
83 | 43 | public function getCount() { |
84 | 44 | return static.counter; |
85 | 45 | } |
86 | 46 | } |
87 | 47 | ``` |
88 | 48 |
|
89 | | -Here, the variable `counter` is defined in the static scope. This means that all instances of Example1.cfc share this variable. |
90 | | - |
91 | | -2. In the following example, we call the Example1() function three times. Each time, the `counter` variable is incremented and shared across all instances. |
| 49 | +When multiple instances of this component are created, the `counter` variable is incremented across all instances: |
92 | 50 |
|
93 | | -```luceescript |
94 | | -// example1.cfm |
95 | | -new Example1(); |
96 | | -new Example1(); |
97 | | -new Example1(); |
98 | 51 | ``` |
99 | | - |
100 | | -## Example 3 |
101 | | - |
102 | | -1. Another example is using the static scope to store the result of a time-consuming operation that does not need to be recomputed every time. |
103 | | - |
104 | | -```luceescript |
105 | | -// Example2.cfc |
106 | | -Component { |
107 | | - static var data = []; |
108 | | - public function init() { |
109 | | - if (arrayLen(static.data) == 0) { |
110 | | - for (i = 1; i <= 100; i++) { |
111 | | - arrayAppend(static.data, i * i); |
112 | | - } |
113 | | - } |
114 | | - dump(static.data); |
115 | | - } |
116 | | -} |
| 52 | +new Example(); |
| 53 | +new Example(); |
| 54 | +new Example(); |
117 | 55 | ``` |
118 | 56 |
|
119 | | -Here, the array `data` is defined in the static scope, which means it will be computed only once and shared across all instances. |
120 | | - |
121 | | -2. In the following example, we call the Example2() function twice. The array `data` is computed only once and reused in the second instance. |
| 57 | +Each instance shares the same `counter` value, demonstrating the persistent nature of static variables. |
122 | 58 |
|
123 | | -```luceescript |
124 | | -// example2.cfm |
125 | | -new Example2(); |
126 | | -new Example2(); |
127 | | -``` |
128 | | - |
129 | | -## Example 4 |
| 59 | +## Using Static Functions |
130 | 60 |
|
131 | | -1. The static scope can also be used for functions. In this example, we define a static function that is available to all instances. |
| 61 | +A static function can be called directly on the component itself, without needing an instance: |
132 | 62 |
|
133 | | -```luceescript |
134 | | -// Example3.cfc |
135 | | -Component { |
| 63 | +``` |
| 64 | +component { |
136 | 65 | public static function hello() { |
137 | 66 | return "Hello, World!"; |
138 | 67 | } |
139 | 68 | } |
140 | 69 | ``` |
141 | 70 |
|
142 | | -2. In the following example, we call the static function `hello` without creating an instance of Example3. |
| 71 | +Calling a static function without instantiating the component: |
143 | 72 |
|
144 | | -```luceescript |
145 | | -// example3.cfm |
146 | | -dump(Example3::hello()); |
| 73 | +``` |
| 74 | +dump(Example::hello()); |
147 | 75 | ``` |
148 | 76 |
|
149 | | -## Example 5 |
| 77 | +## Static Methods vs. Instance Methods |
150 | 78 |
|
151 | | -1. The static scope can be used to count the number of instances created from a component. |
| 79 | +- **Instance Methods**: Defined without `static` and tied to an object instance. |
| 80 | +- **Static Methods**: Defined with `static` and shared across all instances. |
152 | 81 |
|
153 | | -```luceescript |
154 | | -// Example4.cfc |
155 | | -Component { |
156 | | - static var counter = 0; |
157 | | - public function init() { |
158 | | - static.counter++; |
159 | | - dump(static.counter & " instances used so far"); |
| 82 | +Example: |
| 83 | + |
| 84 | +``` |
| 85 | +component { |
| 86 | + public static function staticMethod() { |
| 87 | + return "I am static"; |
| 88 | + } |
| 89 | + |
| 90 | + public function instanceMethod() { |
| 91 | + return "I am an instance method"; |
160 | 92 | } |
161 | 93 | } |
162 | 94 | ``` |
163 | 95 |
|
164 | | -2. In the following example, we call the Example4() function five times. Each time the function is called, the count of `counter` in the static scope increases. |
| 96 | +Usage: |
165 | 97 |
|
166 | | -```luceescript |
167 | | -// example4.cfm |
168 | | -new Example4(); |
169 | | -new Example4(); |
170 | | -new Example4(); |
171 | | -new Example4(); |
172 | | -new Example4(); |
173 | 98 | ``` |
| 99 | +obj = new Example(); |
| 100 | +dump(obj.instanceMethod()); // Works only on an instance |
174 | 101 |
|
175 | | -## Example 6 |
| 102 | +dump(Example::staticMethod()); // Works without an instance |
| 103 | +``` |
176 | 104 |
|
177 | | -1. We can also use the static scope to store constant data like HOST, PORT. |
| 105 | +## Accessing Static Methods via Instances |
178 | 106 |
|
179 | | -- If we store the instance in the variable scope, you will run into problems when you have a thousand components or it gets loaded a thousand times. This is a waste of time and memory storage. |
180 | | -- The static scope means that a variable only exists once and is independent of how many instances you have. So it is more memory efficient to do it that way. You can also do the same for functions. |
| 107 | +Lucee allows static methods to be accessed the same way as instance methods: |
181 | 108 |
|
182 | | -```luceescript |
183 | | -// Example5.cfc |
184 | | -Component { |
185 | | - static { |
186 | | - static.HOST = "lucee.org"; |
187 | | - static.PORT = 8080; |
188 | | - } |
189 | | - public static function splitFullName(required string fullName) { |
190 | | - var arr = listToArray(fullName, " "); |
191 | | - return {'lastname': arr[1], 'firstname': arr[2]}; |
192 | | - } |
193 | | - public function init(required string fullName) { |
194 | | - variables.fullname = static.splitFullName(fullName); |
195 | | - } |
196 | | - public string function getLastName() { |
197 | | - return variables.fullname.lastname; |
198 | | - } |
199 | | -} |
200 | 109 | ``` |
| 110 | +obj = new Example(); |
| 111 | +dump(obj.staticMethod()); // Outputs: "I am static" |
| 112 | +``` |
| 113 | + |
| 114 | +This means static methods do not require special handling and can be called via an instance or the class itself. |
201 | 115 |
|
202 | | -2. In the following example, we call the Example5() function in two ways. It has a function splitFullName() that does not need to access anything (read or write data from the disks) and a variable scope that doesn't have to be part of the instance. It returns the firstname and lastname. |
| 116 | +## Mocking Static Methods |
203 | 117 |
|
204 | | -```luceescript |
205 | | -// example5.cfm |
206 | | -person = new Example5("Sobchak Walter"); |
207 | | -dump(person.getLastName()); |
208 | | -dump(Example5::splitFullName("Quintana Jesus")); |
| 118 | +A key advantage of Lucee’s implementation is that **static methods can be accessed just like instance methods** and **can be mocked per instance**: |
| 119 | + |
| 120 | +``` |
| 121 | +obj = new Example(); |
| 122 | +obj.staticMethod = function() { |
| 123 | + return "Mocked static method"; |
| 124 | +}; |
| 125 | +
|
| 126 | +dump(Example::staticMethod()); // Outputs: "I am static" |
| 127 | +dump(obj.staticMethod()); // Outputs: "Mocked static method" |
209 | 128 | ``` |
210 | 129 |
|
211 | | -## Footnotes |
| 130 | +This means: |
| 131 | +- Static functions can be dynamically modified per instance without affecting the original class. |
| 132 | +- No need for redundant instance wrappers for testing. |
| 133 | + |
| 134 | +## Benefits of Static Scope |
| 135 | + |
| 136 | +1. **Performance Optimization** – Avoids redundant instantiations. |
| 137 | +2. **Shared State** – Useful for counters, caching, and global configurations. |
| 138 | +3. **Mocking Flexibility** – Allows instance-level modifications for testing while keeping the original static method intact. |
| 139 | +4. **Overlay vs. Overwrite** – When an instance function is redefined, it **overwrites** the original implementation for that instance. With static methods, defining an instance-level function of the same name **overlays** the static method for that instance only, while the original static method remains accessible via the class. |
| 140 | + |
| 141 | +## Conclusion |
212 | 142 |
|
213 | | -[Lucee 5 features reviewed: static](https://dev.lucee.org/t/lucee-5-features-reviewed-static/433) |
| 143 | +Static scope in Lucee enables shared variables and functions across instances, improving efficiency and making testing more flexible. By understanding how to use static variables, functions, and mocking techniques, developers can write cleaner, more maintainable code. |
214 | 144 |
|
215 | | -[Video: Lucee Static Scopes in Component Code](https://www.youtube.com/watch?v=B5ILIAbXBzo&feature=youtu.be) |
0 commit comments