Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/ast/classconstant.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,17 @@ const ClassConstant = ConstantStatement.extends(
* @return {void}
*/
ClassConstant.prototype.parseFlags = function (flags) {
if (flags[0] === -1) {
const getVis = flags[0][0];
if (getVis === -1) {
this.visibility = IS_UNDEFINED;
} else if (flags[0] === null) {
} else if (getVis === null) {
/* istanbul ignore next */
this.visibility = null;
} else if (flags[0] === 0) {
} else if (getVis === 0) {
this.visibility = IS_PUBLIC;
} else if (flags[0] === 1) {
} else if (getVis === 1) {
this.visibility = IS_PROTECTED;
} else if (flags[0] === 2) {
} else if (getVis === 2) {
this.visibility = IS_PRIVATE;
}
this.final = flags[2] === 2;
Expand Down
17 changes: 9 additions & 8 deletions src/ast/declaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ const IS_PUBLIC = "public";
const IS_PROTECTED = "protected";
const IS_PRIVATE = "private";

const VISIBILITY_MAP = [IS_PUBLIC, IS_PROTECTED, IS_PRIVATE];

/**
* A declaration statement (function, class, interface...)
* @constructor Declaration
* @memberOf module:php-parser
* @extends {Statement}
* @property {Identifier|string} name
* @property {string|null} visibilitySet
*/
const Declaration = Statement.extends(
KIND,
Expand All @@ -41,19 +44,17 @@ Declaration.prototype.parseFlags = function (flags) {
this.isFinal = flags[2] === 2;
this.isReadonly = flags[3] === 1;
if (this.kind !== "class") {
if (flags[0] === -1) {
const [getVis, setVis] = flags[0];
if (getVis === -1) {
this.visibility = IS_UNDEFINED;
} else if (flags[0] === null) {
} else if (getVis === null) {
/* istanbul ignore next */
this.visibility = null;
} else if (flags[0] === 0) {
this.visibility = IS_PUBLIC;
} else if (flags[0] === 1) {
this.visibility = IS_PROTECTED;
} else if (flags[0] === 2) {
this.visibility = IS_PRIVATE;
} else {
this.visibility = VISIBILITY_MAP[getVis];
}
this.isStatic = flags[1] === 1;
this.visibilitySet = setVis !== -1 ? VISIBILITY_MAP[setVis] : null;
}
};

Expand Down
3 changes: 3 additions & 0 deletions src/ast/parameter.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const KIND = "parameter";
* @property {AttrGroup[]} attrGroups
* @property {MODIFIER_PUBLIC|MODIFIER_PROTECTED|MODIFIER_PRIVATE} flags
* @property {PropertyHook[]} hooks
* @property {MODIFIER_PUBLIC|MODIFIER_PROTECTED|MODIFIER_PRIVATE} flagsSet
*/
module.exports = Declaration.extends(
KIND,
Expand All @@ -47,6 +48,7 @@ module.exports = Declaration.extends(
nullable,
flags,
hooks,
flagsSet,
docs,
location,
) {
Expand All @@ -59,6 +61,7 @@ module.exports = Declaration.extends(
this.nullable = nullable;
this.flags = flags || 0;
this.hooks = hooks || [];
this.flagsSet = flagsSet || 0;
this.attrGroups = [];
},
);
17 changes: 9 additions & 8 deletions src/ast/propertystatement.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ const IS_PUBLIC = "public";
const IS_PROTECTED = "protected";
const IS_PRIVATE = "private";

const VISIBILITY_MAP = [IS_PUBLIC, IS_PROTECTED, IS_PRIVATE];

/**
* Declares a properties into the current scope
* @constructor PropertyStatement
* @memberOf module:php-parser
* @extends {Statement}
* @property {Property[]} properties
* @property {string|null} visibility
* @property {string|null} visibilitySet
* @property {boolean} isStatic
* @property {boolean} isAbstract
* @property {boolean} isFinal
Expand All @@ -41,21 +44,19 @@ const PropertyStatement = Statement.extends(
* @return {void}
*/
PropertyStatement.prototype.parseFlags = function (flags) {
if (flags[0] === -1) {
const [getVis, setVis] = flags[0];
if (getVis === -1) {
this.visibility = IS_UNDEFINED;
} else if (flags[0] === null) {
} else if (getVis === null) {
this.visibility = null;
} else if (flags[0] === 0) {
this.visibility = IS_PUBLIC;
} else if (flags[0] === 1) {
this.visibility = IS_PROTECTED;
} else if (flags[0] === 2) {
this.visibility = IS_PRIVATE;
} else {
this.visibility = VISIBILITY_MAP[getVis];
}

this.isStatic = flags[1] === 1;
this.isAbstract = flags[2] === 1;
this.isFinal = flags[2] === 2;
this.visibilitySet = setVis !== -1 ? VISIBILITY_MAP[setVis] : null;
};

module.exports = PropertyStatement;
7 changes: 4 additions & 3 deletions src/ast/traitalias.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ module.exports = Node.extends(
this.as = as;
this.visibility = IS_UNDEFINED;
if (flags) {
if (flags[0] === 0) {
const getVis = flags[0][0];
if (getVis === 0) {
this.visibility = IS_PUBLIC;
} else if (flags[0] === 1) {
} else if (getVis === 1) {
this.visibility = IS_PROTECTED;
} else if (flags[0] === 2) {
} else if (getVis === 2) {
this.visibility = IS_PRIVATE;
}
}
Expand Down
135 changes: 83 additions & 52 deletions src/parser/class.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ module.exports = {

// check constant
if (this.token === this.tok.T_CONST) {
if (flags[0][1] !== -1) {
this.raiseError("Cannot use asymmetric visibility on constants");
}
const constants = this.read_constant_list(flags, attrs);
if (this.expect(";")) {
this.next();
Expand All @@ -126,7 +129,7 @@ module.exports = {
// jump over T_VAR then land on T_VARIABLE
if (allow_variables && this.token === this.tok.T_VAR) {
this.next().expect(this.tok.T_VARIABLE);
flags[0] = null; // public (as null)
flags[0][0] = null; // public (as null)
flags[1] = 0; // non static var
}

Expand Down Expand Up @@ -388,69 +391,94 @@ module.exports = {
/*
* Read member flags
* @return array
* 1st index : 0 => public, 1 => protected, 2 => private
* 1st index : [get, set] visibility tuple
* get/set: -1 => no visibility, 0 => public, 1 => protected, 2 => private
* 2nd index : 0 => instance member, 1 => static member
* 3rd index : 0 => normal, 1 => abstract member, 2 => final member
* 4th index : 0 => no readonly, 1 => readonly
*/
read_member_flags(asInterface) {
const result = [-1, -1, -1, -1];
if (this.is("T_MEMBER_FLAGS")) {
let idx = 0,
val = 0;
do {
switch (this.token) {
case this.tok.T_PUBLIC:
idx = 0;
val = 0;
break;
case this.tok.T_PROTECTED:
idx = 0;
val = 1;
break;
case this.tok.T_PRIVATE:
idx = 0;
val = 2;
break;
case this.tok.T_STATIC:
idx = 1;
val = 1;
break;
case this.tok.T_ABSTRACT:
idx = 2;
val = 1;
break;
case this.tok.T_FINAL:
idx = 2;
val = 2;
break;
case this.tok.T_READ_ONLY:
idx = 3;
val = 1;
break;
}
if (asInterface) {
if (idx === 0 && val === 2) {
const result = [[-1, -1], 0, 0, 0];
const seen = new Set();
while (this.is("T_MEMBER_FLAGS")) {
let idx = -1,
val = -1;
switch (this.token) {
case this.tok.T_PUBLIC:
case this.tok.T_PROTECTED:
case this.tok.T_PRIVATE: {
idx = 0;
val =
this.token === this.tok.T_PUBLIC
? 0
: this.token === this.tok.T_PROTECTED
? 1
: 2;
if (asInterface && val === 2) {
// an interface can't be private
this.expect([this.tok.T_PUBLIC, this.tok.T_PROTECTED]);
val = -1;
} else if (idx === 2 && val === 1) {
// an interface cant be abstract
}
this.next(); // consume the visibility keyword
if (this.version >= 804 && this.token === "(") {
// visibility(set) modifier: e.g. private(set)
this.next(); // consume '('
if (this.token !== this.tok.T_STRING || this.text() !== "set") {
this.error("set");
} else {
this.next(); // consume 'set'
}
if (this.expect(")")) {
this.next(); // consume ')'
}
if (seen.has("set")) {
this.error(); // set modifier already defined
} else if (val !== -1) {
seen.add("set");
result[0][1] = val;
}
continue;
}
if (seen.has(idx)) {
this.error();
val = -1;
} else if (val !== -1) {
seen.add(idx);
result[0][0] = val;
}
continue;
}
if (result[idx] !== -1) {
// already defined flag
this.error();
} else if (val !== -1) {
result[idx] = val;
}
} while (this.next().is("T_MEMBER_FLAGS"));
case this.tok.T_STATIC:
idx = 1;
val = 1;
break;
case this.tok.T_ABSTRACT:
idx = 2;
val = 1;
break;
case this.tok.T_FINAL:
idx = 2;
val = 2;
break;
case this.tok.T_READ_ONLY:
idx = 3;
val = 1;
break;
}
if (asInterface && idx === 2 && val === 1) {
// an interface can't be abstract
this.error();
val = -1;
}
if (seen.has(idx)) {
// already defined flag
this.error();
} else if (val !== -1) {
seen.add(idx);
result[idx] = val;
}
this.next();
}

if (result[1] === -1) result[1] = 0;
if (result[2] === -1) result[2] = 0;
if (result[3] === -1) result[3] = 0;
return result;
},

Expand Down Expand Up @@ -581,6 +609,9 @@ module.exports = {

// check constant
if (this.token === this.tok.T_CONST) {
if (flags[0][1] !== -1) {
this.raiseError("Cannot use asymmetric visibility on constants");
}
const constants = this.read_constant_list(flags, attrs);
if (this.expect(";")) {
this.next();
Expand Down
Loading