Allow parameter promotion in constructors
[hiphop-php.git] / hphp / compiler / statement / method_statement.cpp
blobb39758fa1c64ece6c6f99459e38bc8af278e14f9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/compiler/statement/method_statement.h"
18 #include "hphp/compiler/statement/return_statement.h"
19 #include "hphp/compiler/statement/statement_list.h"
20 #include "hphp/compiler/statement/try_statement.h"
21 #include "hphp/compiler/statement/label_statement.h"
22 #include "hphp/compiler/statement/goto_statement.h"
23 #include "hphp/compiler/statement/exp_statement.h"
24 #include "hphp/compiler/statement/switch_statement.h"
25 #include "hphp/compiler/statement/case_statement.h"
26 #include "hphp/compiler/statement/catch_statement.h"
28 #include "hphp/compiler/expression/modifier_expression.h"
29 #include "hphp/compiler/expression/expression_list.h"
30 #include "hphp/compiler/expression/constant_expression.h"
31 #include "hphp/compiler/expression/parameter_expression.h"
32 #include "hphp/compiler/expression/assignment_expression.h"
33 #include "hphp/compiler/expression/simple_variable.h"
34 #include "hphp/compiler/expression/closure_expression.h"
36 #include "hphp/compiler/analysis/ast_walker.h"
37 #include "hphp/compiler/analysis/analysis_result.h"
38 #include "hphp/compiler/analysis/code_error.h"
39 #include "hphp/compiler/analysis/file_scope.h"
40 #include "hphp/compiler/analysis/variable_table.h"
41 #include "hphp/compiler/analysis/class_scope.h"
42 #include "hphp/compiler/analysis/function_scope.h"
44 #include "hphp/compiler/option.h"
45 #include "hphp/compiler/builtin_symbols.h"
46 #include "hphp/compiler/analysis/alias_manager.h"
48 #include "hphp/runtime/base/complex_types.h"
50 #include "hphp/util/parser/parser.h"
51 #include "hphp/util/util.h"
53 using namespace HPHP;
54 using std::map;
56 ///////////////////////////////////////////////////////////////////////////////
57 // constructors/destructors
59 MethodStatement::MethodStatement
60 (STATEMENT_CONSTRUCTOR_BASE_PARAMETERS,
61 ModifierExpressionPtr modifiers, bool ref, const string &name,
62 ExpressionListPtr params, const std::string &retTypeConstraint,
63 StatementListPtr stmt, int attr, const string &docComment,
64 ExpressionListPtr attrList, bool method /* = true */)
65 : Statement(STATEMENT_CONSTRUCTOR_BASE_PARAMETER_VALUES),
66 m_method(method), m_ref(ref), m_hasCallToGetArgs(false), m_attribute(attr),
67 m_cppLength(-1), m_modifiers(modifiers),
68 m_originalName(name), m_params(params),
69 m_retTypeConstraint(retTypeConstraint), m_stmt(stmt),
70 m_docComment(docComment), m_attrList(attrList) {
71 m_name = Util::toLower(name);
72 checkParameters();
75 MethodStatement::MethodStatement
76 (STATEMENT_CONSTRUCTOR_PARAMETERS,
77 ModifierExpressionPtr modifiers, bool ref, const string &name,
78 ExpressionListPtr params, const std::string &retTypeConstraint,
79 StatementListPtr stmt,
80 int attr, const string &docComment, ExpressionListPtr attrList,
81 bool method /* = true */)
82 : Statement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(MethodStatement)),
83 m_method(method), m_ref(ref), m_hasCallToGetArgs(false), m_attribute(attr),
84 m_cppLength(-1), m_modifiers(modifiers), m_originalName(name),
85 m_params(params), m_retTypeConstraint(retTypeConstraint),
86 m_stmt(stmt), m_docComment(docComment), m_attrList(attrList) {
87 m_name = Util::toLower(name);
88 checkParameters();
91 StatementPtr MethodStatement::clone() {
92 MethodStatementPtr stmt(new MethodStatement(*this));
93 stmt->m_stmt = Clone(m_stmt);
94 stmt->m_params = Clone(m_params);
95 stmt->m_modifiers = Clone(m_modifiers);
96 return stmt;
99 string MethodStatement::getFullName() const {
100 if (m_className.empty()) return m_name;
101 return m_className + "::" + m_name;
104 string MethodStatement::getOriginalFullName() const {
105 if (m_originalClassName.empty()) return m_originalName;
106 return m_originalClassName + "::" + m_originalName;
109 string MethodStatement::getOriginalFullNameForInjection() const {
110 FunctionScopeRawPtr funcScope = getFunctionScope();
111 string injectionName;
112 if (getGeneratorFunc()) {
113 injectionName = funcScope->isClosureGenerator() ?
114 m_originalName :
115 m_originalName + "{continuation}";
116 } else if (getOrigGeneratorFunc()) {
117 bool needsOrig = !funcScope->getOrigGenFS()->isClosure();
118 injectionName = needsOrig ?
119 getOrigGeneratorFunc()->getOriginalName() :
120 m_originalName;
121 } else {
122 injectionName = m_originalName;
124 return m_originalClassName.empty() ?
125 injectionName :
126 m_originalClassName + "::" + injectionName;
129 bool MethodStatement::isRef(int index /* = -1 */) const {
130 if (index == -1) return m_ref;
131 assert(index >= 0 && index < m_params->getCount());
132 ParameterExpressionPtr param =
133 dynamic_pointer_cast<ParameterExpression>((*m_params)[index]);
134 return param->isRef();
137 int MethodStatement::getRecursiveCount() const {
138 return m_stmt ? m_stmt->getRecursiveCount() : 0;
141 ///////////////////////////////////////////////////////////////////////////////
142 // parser functions
144 FunctionScopePtr MethodStatement::onInitialParse(AnalysisResultConstPtr ar,
145 FileScopePtr fs) {
146 int minParam, maxParam;
147 ConstructPtr self = shared_from_this();
148 minParam = maxParam = 0;
149 bool hasRef = false;
150 if (m_params) {
151 std::set<string> names, allDeclNames;
152 int i = 0;
153 maxParam = m_params->getCount();
154 for (i = maxParam; i--; ) {
155 ParameterExpressionPtr param =
156 dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
157 if (param->isRef()) hasRef = true;
158 if (!param->isOptional()) {
159 if (!minParam) minParam = i + 1;
160 } else if (minParam && !param->hasTypeHint()) {
161 Compiler::Error(Compiler::RequiredAfterOptionalParam, param);
163 allDeclNames.insert(param->getName());
166 for (i = maxParam-1; i >= 0; i--) {
167 ParameterExpressionPtr param =
168 dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
169 if (names.find(param->getName()) != names.end()) {
170 Compiler::Error(Compiler::RedundantParameter, param);
171 for (int j = 0; j < 1000; j++) {
172 string name = param->getName() + lexical_cast<string>(j);
173 if (names.find(name) == names.end() &&
174 allDeclNames.find(name) == allDeclNames.end()) {
175 param->rename(name);
176 break;
180 names.insert(param->getName());
184 if (hasRef || m_ref) {
185 m_attribute |= FileScope::ContainsReference;
188 vector<UserAttributePtr> attrs;
189 if (m_attrList) {
190 for (int i = 0; i < m_attrList->getCount(); ++i) {
191 UserAttributePtr a =
192 dynamic_pointer_cast<UserAttribute>((*m_attrList)[i]);
193 attrs.push_back(a);
197 StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this());
198 FunctionScopePtr funcScope
199 (new FunctionScope(ar, m_method, m_name, stmt, m_ref, minParam, maxParam,
200 m_modifiers, m_attribute, m_docComment, fs, attrs));
201 if (!m_stmt) {
202 funcScope->setVirtual();
204 setBlockScope(funcScope);
206 funcScope->setParamCounts(ar, -1, -1);
207 return funcScope;
210 void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
211 ClassScopePtr classScope) {
213 if (m_modifiers) {
214 if (classScope->isInterface()) {
215 if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
216 m_modifiers->isAbstract() || m_modifiers->isFinal()) {
217 m_modifiers->parseTimeFatal(
218 Compiler::InvalidAttribute,
219 "Access type for interface method %s::%s() must be omitted",
220 classScope->getOriginalName().c_str(), getOriginalName().c_str());
223 if (m_modifiers->isAbstract()) {
224 if (m_modifiers->isPrivate() || m_modifiers->isFinal()) {
225 m_modifiers->parseTimeFatal(
226 Compiler::InvalidAttribute,
227 "Cannot declare abstract method %s::%s() %s",
228 classScope->getOriginalName().c_str(),
229 getOriginalName().c_str(),
230 m_modifiers->isPrivate() ? "private" : "final");
232 if (!classScope->isInterface() && !classScope->isAbstract()) {
233 /* note that classScope->isAbstract() returns true for traits */
234 m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
235 "Class %s contains abstract method %s and "
236 "must therefore be declared abstract",
237 classScope->getOriginalName().c_str(),
238 getOriginalName().c_str());
240 if (getStmts()) {
241 parseTimeFatal(Compiler::InvalidAttribute,
242 "Abstract method %s::%s() cannot contain body",
243 classScope->getOriginalName().c_str(),
244 getOriginalName().c_str());
248 if ((!m_modifiers || !m_modifiers->isAbstract()) &&
249 !getStmts() && !classScope->isInterface()) {
250 parseTimeFatal(Compiler::InvalidAttribute,
251 "Non-abstract method %s::%s() must contain body",
252 classScope->getOriginalName().c_str(),
253 getOriginalName().c_str());
256 FunctionScopeRawPtr fs = getFunctionScope();
258 classScope->addFunction(ar, fs);
260 m_className = classScope->getName();
261 m_originalClassName = classScope->getOriginalName();
263 setSpecialMethod(classScope);
265 if (Option::DynamicInvokeFunctions.find(getFullName()) !=
266 Option::DynamicInvokeFunctions.end()) {
267 fs->setDynamicInvoke();
269 if (m_params) {
270 for (int i = 0; i < m_params->getCount(); i++) {
271 ParameterExpressionPtr param =
272 dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
273 param->parseHandler(classScope);
276 FunctionScope::RecordFunctionInfo(m_name, fs);
279 void MethodStatement::fixupSelfAndParentTypehints(ClassScopePtr scope) {
280 if (m_params) {
281 for (int i = 0; i < m_params->getCount(); i++) {
282 ParameterExpressionPtr param =
283 dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
284 param->fixupSelfAndParentTypehints(scope);
289 void MethodStatement::setSpecialMethod(ClassScopePtr classScope) {
290 if (m_name.size() < 2 || m_name.substr(0,2) != "__") {
291 return;
293 int numArgs = -1;
294 bool isStatic = false;
295 if (m_name == "__construct") {
296 classScope->setAttribute(ClassScope::HasConstructor);
297 } else if (m_name == "__destruct") {
298 classScope->setAttribute(ClassScope::HasDestructor);
299 } else if (m_name == "__call") {
300 classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
301 numArgs = 2;
302 } else if (m_name == "__get") {
303 classScope->setAttribute(ClassScope::HasUnknownPropGetter);
304 numArgs = 1;
305 } else if (m_name == "__set") {
306 classScope->setAttribute(ClassScope::HasUnknownPropSetter);
307 numArgs = 2;
308 } else if (m_name == "__isset") {
309 classScope->setAttribute(ClassScope::HasUnknownPropTester);
310 numArgs = 1;
311 } else if (m_name == "__unset") {
312 classScope->setAttribute(ClassScope::HasPropUnsetter);
313 numArgs = 1;
314 } else if (m_name == "__call") {
315 classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
316 numArgs = 2;
317 } else if (m_name == "__callstatic") {
318 classScope->setAttribute(ClassScope::HasUnknownStaticMethodHandler);
319 numArgs = 2;
320 isStatic = true;
321 } else if (m_name == "__invoke") {
322 classScope->setAttribute(ClassScope::HasInvokeMethod);
323 } else if (m_name == "__tostring") {
324 numArgs = 0;
326 if (numArgs >= 0) {
327 // Fatal if the number of arguments is wrong
328 int n = m_params ? m_params->getCount() : 0;
329 if (numArgs != n) {
330 parseTimeFatal(Compiler::InvalidMagicMethod,
331 "Method %s::%s() must take exactly %d argument%s",
332 m_originalClassName.c_str(), m_originalName.c_str(),
333 numArgs, (numArgs == 1) ? "" : "s");
335 // Fatal if any arguments are pass by reference
336 if (m_params && hasRefParam()) {
337 parseTimeFatal(Compiler::InvalidMagicMethod,
338 "Method %s::%s() cannot take arguments by reference",
339 m_originalClassName.c_str(), m_originalName.c_str());
341 // Fatal if protected/private or if the staticness is wrong
342 if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
343 m_modifiers->isStatic() != isStatic) {
344 parseTimeFatal(Compiler::InvalidMagicMethod,
345 "Method %s::%s() must have public visibility and %sbe static",
346 m_originalClassName.c_str(), m_originalName.c_str(),
347 isStatic ? "" : "cannot ");
352 void MethodStatement::addTraitMethodToScope(AnalysisResultConstPtr ar,
353 ClassScopePtr classScope) {
354 FunctionScopeRawPtr funcScope = getFunctionScope();
355 classScope->addFunction(ar, funcScope);
356 setSpecialMethod(classScope);
357 FunctionScope::RecordFunctionInfo(m_name, funcScope);
360 ///////////////////////////////////////////////////////////////////////////////
361 // static analysis functions
363 int MethodStatement::getLocalEffects() const {
364 if (m_method) return NoEffect;
365 FunctionScopeRawPtr scope = getFunctionScope();
366 return scope->isVolatile() ? OtherEffect | CanThrow : NoEffect;
369 void MethodStatement::analyzeProgram(AnalysisResultPtr ar) {
370 FunctionScopeRawPtr funcScope = getFunctionScope();
372 if (m_params) {
373 m_params->analyzeProgram(ar);
375 if (m_stmt) m_stmt->analyzeProgram(ar);
377 if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
378 funcScope->setParamSpecs(ar);
379 if (funcScope->isGenerator()) {
380 MethodStatementRawPtr orig = getOrigGeneratorFunc();
381 VariableTablePtr variables = funcScope->getVariables();
383 Symbol *cont = variables->getSymbol(CONTINUATION_OBJECT_NAME);
384 cont->setHidden();
386 orig->getFunctionScope()->addUse(funcScope, BlockScope::UseKindClosure);
387 orig->getFunctionScope()->setContainsBareThis(
388 funcScope->containsBareThis(), funcScope->containsRefThis());
389 orig->getFunctionScope()->setContainsThis(funcScope->containsThis());
391 if (ExpressionListPtr params = orig->getParams()) {
392 for (int i = 0; i < params->getCount(); ++i) {
393 auto param = dynamic_pointer_cast<ParameterExpression>((*params)[i]);
394 Symbol *gp = variables->addDeclaredSymbol(param->getName(), param);
395 gp->setGeneratorParameter();
396 if (param->isRef()) {
397 gp->setRefGeneratorParameter();
398 gp->setReferenced();
403 if (ClosureExpressionRawPtr closure = orig->getContainingClosure()) {
404 if (ExpressionListPtr cvars = closure->getClosureVariables()) {
405 for (int i = 0; i < cvars->getCount(); ++i) {
406 auto param = dynamic_pointer_cast<ParameterExpression>((*cvars)[i]);
407 Symbol *gp = variables->addDeclaredSymbol(
408 param->getName(), ConstructPtr());
409 gp->setGeneratorParameter();
410 if (param->isRef()) {
411 gp->setRefGeneratorParameter();
412 gp->setReferenced();
418 if (funcScope->isSepExtension() ||
419 Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) {
420 funcScope->setDynamic();
422 // TODO: this may have to expand to a concept of "virtual" functions...
423 if (m_method) {
424 funcScope->disableInline();
425 if (m_name.length() > 2 && m_name.substr(0,2) == "__") {
426 bool magic = true;
427 int paramCount = 0;
428 if (m_name == "__destruct") {
429 funcScope->setOverriding(Type::Variant);
430 } else if (m_name == "__call") {
431 funcScope->setOverriding(Type::Variant, Type::String, Type::Array);
432 paramCount = 2;
433 } else if (m_name == "__set") {
434 funcScope->setOverriding(Type::Variant, Type::String, Type::Variant);
435 paramCount = 2;
436 } else if (m_name == "__get") {
437 funcScope->setOverriding(Type::Variant, Type::String);
438 paramCount = 1;
439 } else if (m_name == "__isset") {
440 funcScope->setOverriding(Type::Boolean, Type::String);
441 paramCount = 1;
442 } else if (m_name == "__unset") {
443 funcScope->setOverriding(Type::Variant, Type::String);
444 paramCount = 1;
445 } else if (m_name == "__sleep") {
446 funcScope->setOverriding(Type::Variant);
447 } else if (m_name == "__wakeup") {
448 funcScope->setOverriding(Type::Variant);
449 } else if (m_name == "__set_state") {
450 funcScope->setOverriding(Type::Variant, Type::Variant);
451 paramCount = 1;
452 } else if (m_name == "__tostring") {
453 funcScope->setOverriding(Type::String);
454 } else if (m_name == "__clone") {
455 funcScope->setOverriding(Type::Variant);
456 } else {
457 paramCount = -1;
458 if (m_name != "__construct") {
459 magic = false;
462 if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) {
463 Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this());
464 magic = false;
466 if (magic) funcScope->setMagicMethod();
468 // ArrayAccess methods
469 else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") {
470 if (m_name == "offsetexists") {
471 funcScope->setOverriding(Type::Boolean, Type::Variant);
472 } else if (m_name == "offsetget") {
473 funcScope->setOverriding(Type::Variant, Type::Variant);
474 } else if (m_name == "offsetset") {
475 funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant);
476 } else if (m_name == "offsetunset") {
477 funcScope->setOverriding(Type::Variant, Type::Variant);
481 } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
482 TypePtr ret = funcScope->getReturnType();
483 if (ret && ret->isSpecificObject()) {
484 FileScopePtr fs = getFileScope();
485 if (fs) fs->addClassDependency(ar, ret->getName());
487 if (!getFunctionScope()->usesLSB()) {
488 if (StatementPtr orig = getOrigGeneratorFunc()) {
489 orig->getFunctionScope()->clearUsesLSB();
495 ConstructPtr MethodStatement::getNthKid(int n) const {
496 switch (n) {
497 case 0:
498 return m_modifiers;
499 case 1:
500 return m_params;
501 case 2:
502 return m_stmt;
503 default:
504 assert(false);
505 break;
507 return ConstructPtr();
510 int MethodStatement::getKidCount() const {
511 return 3;
514 void MethodStatement::setNthKid(int n, ConstructPtr cp) {
515 switch (n) {
516 case 0:
517 m_modifiers = boost::dynamic_pointer_cast<ModifierExpression>(cp);
518 break;
519 case 1:
520 m_params = boost::dynamic_pointer_cast<ExpressionList>(cp);
521 break;
522 case 2:
523 m_stmt = boost::dynamic_pointer_cast<StatementList>(cp);
524 break;
525 default:
526 assert(false);
527 break;
531 void MethodStatement::inferTypes(AnalysisResultPtr ar) {
534 void MethodStatement::inferFunctionTypes(AnalysisResultPtr ar) {
535 IMPLEMENT_INFER_AND_CHECK_ASSERT(getFunctionScope());
537 FunctionScopeRawPtr funcScope = getFunctionScope();
538 bool pseudoMain = funcScope->inPseudoMain();
540 if (m_stmt && funcScope->isFirstPass()) {
541 if (pseudoMain ||
542 funcScope->getReturnType() ||
543 m_stmt->hasRetExp()) {
544 bool lastIsReturn = false;
545 if (m_stmt->getCount()) {
546 StatementPtr lastStmt = (*m_stmt)[m_stmt->getCount()-1];
547 if (lastStmt->is(Statement::KindOfReturnStatement)) {
548 lastIsReturn = true;
551 if (!lastIsReturn) {
552 ExpressionPtr constant =
553 makeScalarExpression(ar, funcScope->inPseudoMain() ?
554 Variant(1) :
555 Variant(Variant::NullInit()));
556 ReturnStatementPtr returnStmt =
557 ReturnStatementPtr(
558 new ReturnStatement(getScope(), getLocation(), constant));
559 m_stmt->addElement(returnStmt);
564 if (m_params) {
565 m_params->inferAndCheck(ar, Type::Any, false);
568 // must also include params and use vars if this is a generator. note: we are
569 // OK reading the params from the AST nodes of the original generator
570 // function, since we have the dependency links set up
571 if (funcScope->isGenerator()) {
572 // orig function params
573 MethodStatementRawPtr m = getOrigGeneratorFunc();
574 assert(m);
576 VariableTablePtr variables = funcScope->getVariables();
577 ExpressionListPtr params = m->getParams();
578 if (params) {
579 for (int i = 0; i < params->getCount(); i++) {
580 ParameterExpressionPtr param =
581 dynamic_pointer_cast<ParameterExpression>((*params)[i]);
582 const string &name = param->getName();
583 assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
584 variables->addParamLike(name, param->getType(), ar, param,
585 funcScope->isFirstPass());
589 // use vars
590 ExpressionListPtr useVars = m->getFunctionScope()->getClosureVars();
591 if (useVars) {
592 for (int i = 0; i < useVars->getCount(); i++) {
593 ParameterExpressionPtr param =
594 dynamic_pointer_cast<ParameterExpression>((*useVars)[i]);
595 const string &name = param->getName();
596 assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
597 variables->addParamLike(name, param->getType(), ar, param,
598 funcScope->isFirstPass());
603 if (m_stmt) {
604 m_stmt->inferTypes(ar);
608 ///////////////////////////////////////////////////////////////////////////////
609 // code generation functions
611 void MethodStatement::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
612 FunctionScopeRawPtr funcScope = getFunctionScope();
614 m_modifiers->outputPHP(cg, ar);
615 cg_printf(" function ");
616 if (m_ref) cg_printf("&");
617 if (!ParserBase::IsClosureOrContinuationName(m_name)) {
618 cg_printf("%s", m_originalName.c_str());
620 cg_printf("(");
621 if (m_params) m_params->outputPHP(cg, ar);
622 if (m_stmt) {
623 cg_indentBegin(") {\n");
624 funcScope->outputPHP(cg, ar);
625 m_stmt->outputPHP(cg, ar);
626 cg_indentEnd("}\n");
627 } else {
628 cg_printf(");\n");
632 bool MethodStatement::hasRefParam() {
633 for (int i = 0; i < m_params->getCount(); i++) {
634 ParameterExpressionPtr param =
635 dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
636 if (param->isRef()) return true;
638 return false;
641 void MethodStatement::checkParameters() {
642 // only allow paramenter modifiers (public, private, protected)
643 // on constructor for promotion
644 if (!m_params) {
645 return;
647 bool isCtor = m_name == "__construct";
648 for (int i = 0; i < m_params->getCount(); i++) {
649 auto param =
650 dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
651 switch (param->getModifier()) {
652 case 0:
653 continue;
654 case T_PUBLIC:
655 case T_PRIVATE:
656 case T_PROTECTED:
657 if (isCtor) {
658 continue;
660 default:
661 if (isCtor) {
662 param->parseTimeFatal(Compiler::InvalidAttribute,
663 "Invalid modifier on __construct, only public, "
664 "private or protected allowed");
665 } else {
666 param->parseTimeFatal(Compiler::InvalidAttribute,
667 "Parameters modifiers not allowed on methods");