/******************************************************************************* * Copyright (c) 2009, 2015, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.core.index; import java.util.*; import java.util.Map.Entry; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.eclipse.dltk.ast.ASTListNode; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.Modifiers; import org.eclipse.dltk.ast.declarations.*; import org.eclipse.dltk.ast.expressions.CallArgumentsList; import org.eclipse.dltk.ast.expressions.CallExpression; import org.eclipse.dltk.ast.expressions.Expression; import org.eclipse.dltk.ast.expressions.Literal; import org.eclipse.dltk.ast.references.ConstantReference; import org.eclipse.dltk.ast.references.SimpleReference; import org.eclipse.dltk.ast.references.TypeReference; import org.eclipse.dltk.ast.references.VariableReference; import org.eclipse.dltk.ast.statements.Statement; import org.eclipse.dltk.core.Flags; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.index2.IIndexingRequestor; import org.eclipse.dltk.core.index2.IIndexingRequestor.DeclarationInfo; import org.eclipse.dltk.core.index2.IIndexingRequestor.ReferenceInfo; import org.eclipse.php.core.compiler.IPHPModifiers; import org.eclipse.php.core.compiler.ast.nodes.*; import org.eclipse.php.core.compiler.ast.nodes.PHPDocTag.TagKind; import org.eclipse.php.core.index.PHPIndexingVisitorExtension; import org.eclipse.php.internal.core.Constants; import org.eclipse.php.internal.core.Logger; import org.eclipse.php.internal.core.PHPCoreConstants; import org.eclipse.php.internal.core.PHPCorePlugin; import org.eclipse.php.internal.core.compiler.ReturnDetector; import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils; import org.eclipse.php.internal.core.typeinference.PHPModelUtils; import org.eclipse.php.internal.core.util.MagicMemberUtil; /** * PHP indexing visitor for H2 database * * @author michael * */ public class PHPIndexingVisitor extends PHPIndexingVisitorExtension { private static final String DOLLAR = "$"; //$NON-NLS-1$ private static final String EXTENSION_POINT = "phpIndexingVisitors"; //$NON-NLS-1$ private static final String CLASS_ATTR = "class"; //$NON-NLS-1$ public static final String CONSTRUCTOR_NAME = "__construct"; //$NON-NLS-1$ public static final String PARAMETER_SEPERATOR = "|"; //$NON-NLS-1$ public static final String NULL_VALUE = "#"; //$NON-NLS-1$ public static final char QUALIFIER_SEPERATOR = ';'; public static final char RETURN_TYPE_SEPERATOR = ':'; public static final String DEFAULT_VALUE = " "; //$NON-NLS-1$ public static final String EMPTY_ARRAY_VALUE = "[]"; //$NON-NLS-1$ public static final String ARRAY_VALUE = "[...]"; //$NON-NLS-1$ /** * This should replace the need for fInClass, fInMethod and fCurrentMethod * since in php the type declarations can be nested. */ protected Stack<Declaration> declarations = new Stack<Declaration>(); /** * Deferred elements that where declared in method/function but should * belong to the global scope. */ protected List<ASTNode> deferredDeclarations = new LinkedList<ASTNode>(); /** * Deferred elements that where declared in method/function but should * belong to the namespaced scope. */ protected List<ASTNode> deferredNamespacedDeclarations = new LinkedList<ASTNode>(); /** * This stack contains a set per method, where each set contains all global * variable names declared through 'global' keyword inside this method. */ protected Stack<Set<String>> methodGlobalVars = new Stack<Set<String>>(); /** * This set contains all variable names having global scope. NB: this set * has nothing to do with stack "methodGlobalVars" which only stores * variable names declared through 'global' keyword. */ protected Set<String> globalScopeVars = new HashSet<String>(); /** * Extensions indexing visitor extensions */ private PHPIndexingVisitorExtension[] extensions; private static IConfigurationElement[] extensionElements = Platform.getExtensionRegistry() .getConfigurationElementsFor(PHPCorePlugin.ID, EXTENSION_POINT); protected NamespaceDeclaration fCurrentNamespace; protected Map<String, UsePart> fLastUseParts = new HashMap<String, UsePart>(); protected String fCurrentQualifier; protected Map<String, Integer> fCurrentQualifierCounts = new HashMap<String, Integer>(); protected String fCurrentParent; protected Stack<ASTNode> fNodes = new Stack<ASTNode>(); public PHPIndexingVisitor(IIndexingRequestor requestor, ISourceModule module) { this.requestor = requestor; List<PHPIndexingVisitorExtension> extensions = new ArrayList<PHPIndexingVisitorExtension>( extensionElements.length); for (IConfigurationElement element : extensionElements) { try { PHPIndexingVisitorExtension ext = (PHPIndexingVisitorExtension) element .createExecutableExtension(CLASS_ATTR); ext.setRequestor(requestor); // pass the ISourceModule over to the extension // in case it needs it during indexing ext.setSourceModule(module); extensions.add(ext); } catch (CoreException e) { Logger.logException(e); } } this.extensions = extensions.toArray(new PHPIndexingVisitorExtension[extensions.size()]); } public void modifyDeclaration(ASTNode node, DeclarationInfo info) { for (PHPIndexingVisitorExtension visitor : extensions) { visitor.modifyDeclaration(node, info); } if (info.elementType != IModelElement.PACKAGE_DECLARATION) { if (info.parent == null) { info.parent = PHPCoreConstants.FILE_PARENT; } if (info.qualifier == null) { info.qualifier = PHPCoreConstants.GLOBAL_NAMESPACE; } } if (node != null && (info.flags & IPHPModifiers.AccReturn) == 0 && node instanceof MethodDeclaration) { MethodDeclaration m = (MethodDeclaration) node; if (m.getBody() != null) { ReturnDetector detector = new ReturnDetector(); try { m.getBody().traverse(detector); if (detector.hasReturn()) { info.flags |= IPHPModifiers.AccReturn; } } catch (Exception e) { Logger.logException(e); } } } requestor.addDeclaration(info); } public void modifyReference(ASTNode node, ReferenceInfo info) { for (PHPIndexingVisitorExtension visitor : extensions) { visitor.modifyReference(node, info); } requestor.addReference(info); } /** * See {@link PHPElementResolver#decodeDocInfo(String)} for the decoding * routine. * * @param declaration * Declaration ASTNode * @return decoded PHPDoc info, or <code>null</code> if there's no PHPDoc * info to store. */ protected static String encodeDocInfo(Declaration declaration) { if (declaration instanceof IPHPDocAwareDeclaration) { PHPDocBlock docBlock = ((IPHPDocAwareDeclaration) declaration).getPHPDoc(); if (docBlock != null) { Map<String, String> info = new HashMap<String, String>(); for (PHPDocTag tag : docBlock.getTags()) { if (tag.getTagKind() == TagKind.DEPRECATED) { info.put("d", null); //$NON-NLS-1$ } else if (tag.getTagKind() == TagKind.RETURN) { StringBuilder buf = new StringBuilder(); for (TypeReference ref : tag.getTypeReferences()) { String type = ref.getName().replaceAll(",", "~"); //$NON-NLS-1$ //$NON-NLS-2$ if (buf.length() > 0) { buf.append(','); } buf.append(type); } info.put("r", buf.toString()); //$NON-NLS-1$ } else if (tag.getTagKind() == TagKind.VAR) { if (tag.getTypeReferences().size() > 0) { String typeNames = PHPModelUtils.appendTypeReferenceNames(tag.getTypeReferences()); typeNames = typeNames.replace(Constants.TYPE_SEPARATOR_CHAR, Constants.DOT); info.put("v", typeNames); //$NON-NLS-1$ } } } return encodeDocInfo(info); } } return null; } protected static String encodeDocInfo(Map<String, String> info) { if (info == null) { return null; } StringBuilder buf = new StringBuilder(); for (Entry<String, String> e : info.entrySet()) { if (buf.length() > 0) { buf.append(';'); } buf.append(e.getKey()); if (e.getValue() != null) { buf.append(':').append(e.getValue()); } } return buf.length() > 0 ? buf.toString() : null; } public boolean endvisit(MethodDeclaration method) throws Exception { methodGlobalVars.pop(); declarations.pop(); for (PHPIndexingVisitorExtension visitor : extensions) { visitor.endvisit(method); } endvisitGeneral(method); return true; } public boolean endvisit(TypeDeclaration type) throws Exception { if (type instanceof NamespaceDeclaration) { NamespaceDeclaration namespaceDecl = (NamespaceDeclaration) type; while (deferredNamespacedDeclarations != null && !deferredNamespacedDeclarations.isEmpty()) { final ASTNode[] declarations = deferredNamespacedDeclarations .toArray(new ASTNode[deferredNamespacedDeclarations.size()]); deferredNamespacedDeclarations.clear(); for (ASTNode deferred : declarations) { deferred.traverse(this); } } // resolve more type member declarations resolveMagicMembers(type); fCurrentNamespace = null; // there are no nested namespaces fCurrentQualifier = null; fLastUseParts.clear(); if (namespaceDecl.isGlobal()) { return visitGeneral(type); } } else { // resolve more type member declarations resolveMagicMembers(type); fCurrentParent = null; } declarations.pop(); for (PHPIndexingVisitorExtension visitor : extensions) { visitor.endvisit(type); } endvisitGeneral(type); return true; } @SuppressWarnings("unchecked") public boolean visit(MethodDeclaration method) throws Exception { fNodes.push(method); methodGlobalVars.add(new HashSet<String>()); int modifiers = method.getModifiers(); PHPDocBlock doc = null; if (method instanceof IPHPDocAwareDeclaration) { IPHPDocAwareDeclaration declaration = (IPHPDocAwareDeclaration) method; doc = declaration.getPHPDoc(); } Declaration parentDeclaration = null; if (!declarations.empty()) { parentDeclaration = declarations.peek(); } declarations.push(method); // In case we are entering a nested element - just add to the deferred // list // and get out of the nested element visiting process if (parentDeclaration instanceof MethodDeclaration) { if (fCurrentNamespace == null) { deferredDeclarations.add(method); } else { deferredNamespacedDeclarations.add(method); } return visitGeneral(method); } if (parentDeclaration instanceof InterfaceDeclaration) { method.setModifier(Modifiers.AccAbstract); } String methodName = method.getName(); // Determine whether this method represents constructor: if (methodName.equalsIgnoreCase(CONSTRUCTOR_NAME) || (parentDeclaration instanceof ClassDeclaration && methodName.equalsIgnoreCase(((ClassDeclaration) parentDeclaration).getName()))) { modifiers |= IPHPModifiers.Constructor; } if (parentDeclaration == null || (parentDeclaration instanceof TypeDeclaration && parentDeclaration == fCurrentNamespace)) { modifiers |= Modifiers.AccGlobal; } if (!Flags.isPrivate(modifiers) && !Flags.isProtected(modifiers) && !Flags.isPublic(modifiers)) { modifiers |= Modifiers.AccPublic; } if (doc != null && doc.getTags(TagKind.INHERITDOC).length != 0) { modifiers |= IPHPModifiers.AccInheritdoc; } modifiers = markAsDeprecated(modifiers, method); StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); if (method instanceof PHPMethodDeclaration) { TypeReference returnType = ((PHPMethodDeclaration) method).getReturnType(); if (returnType != null) { metadata.append(returnType.getName()); modifiers |= IPHPModifiers.AccReturn; if (returnType instanceof FullyQualifiedReference) { if (((FullyQualifiedReference) returnType).isNullable()) { modifiers |= IPHPModifiers.AccNullable; } } } } metadata.append(RETURN_TYPE_SEPERATOR); List<Argument> arguments = method.getArguments(); if (arguments != null) { Iterator<Argument> i = arguments.iterator(); while (i.hasNext()) { Argument arg = (Argument) i.next(); String type = NULL_VALUE; if (arg instanceof FormalParameter) { FormalParameter fp = (FormalParameter) arg; if (fp.getParameterType() != null) { if (fp.getParameterType().getName() != null) { type = fp.getParameterType().getName(); } } if (fp.isVariadic()) { modifiers |= IPHPModifiers.AccVariadic; } } if (type == NULL_VALUE && doc != null) { type = getParamType(doc, arg.getName(), type); } metadata.append(type); metadata.append(PARAMETER_SEPERATOR); metadata.append(arg.getName()); metadata.append(PARAMETER_SEPERATOR); String defaultValue = NULL_VALUE; if (arg.getInitialization() != null) { if (arg.getInitialization() instanceof Literal) { Literal scalar = (Literal) arg.getInitialization(); // we need to encode all pipe characters inside string // literals defaultValue = scalar.getValue().replace("&", "&a").replace(PARAMETER_SEPERATOR, "&p"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } else if (arg.getInitialization() instanceof ArrayCreation) { ArrayCreation arrayCreation = (ArrayCreation) arg.getInitialization(); if (arrayCreation.getElements().isEmpty()) { defaultValue = EMPTY_ARRAY_VALUE; } else { defaultValue = ARRAY_VALUE; } } else { defaultValue = DEFAULT_VALUE; } } metadata.append(defaultValue); int paramModifiers = 0; if (arg instanceof FormalParameter) { FormalParameter fp = (FormalParameter) arg; if (fp.getParameterType() instanceof FullyQualifiedReference) { if (((FullyQualifiedReference) fp.getParameterType()).isNullable()) { paramModifiers |= IPHPModifiers.AccNullable; } } } if (arg instanceof FormalParameterByReference) { paramModifiers |= IPHPModifiers.AccReference; } if (paramModifiers != 0) { metadata.append(PARAMETER_SEPERATOR); metadata.append(String.valueOf(paramModifiers)); } if (i.hasNext()) { metadata.append(","); //$NON-NLS-1$ } } } // Add method declaration: modifyDeclaration(method, new DeclarationInfo(IModelElement.METHOD, modifiers, method.sourceStart(), method.sourceEnd() - method.sourceStart(), method.getNameStart(), method.getNameEnd() - method.getNameStart(), methodName, metadata.toString(), encodeDocInfo(method), fCurrentQualifier, fCurrentParent)); for (PHPIndexingVisitorExtension visitor : extensions) { visitor.visit(method); } return visitGeneral(method); } /** * Update modifiers for "deprecated" * * @param modifiers * @param phpDoc * @return */ private int markAsDeprecated(int modifiers, PHPDocBlock phpDoc) { if (phpDoc != null && phpDoc.getTags(TagKind.DEPRECATED).length > 0) { return modifiers | IPHPModifiers.AccDeprecated; } return modifiers; } private int markAsDeprecated(int modifiers, ASTNode node) { if (node instanceof IPHPDocAwareDeclaration) { return markAsDeprecated(modifiers, ((IPHPDocAwareDeclaration) node).getPHPDoc()); } return modifiers; } private String getParamType(PHPDocBlock docBlock, String paramName, String defaultType) { String result = defaultType; if (docBlock != null) { for (PHPDocTag tag : docBlock.getTags(TagKind.PARAM)) { if (tag.isValidParamTag() && tag.getVariableReference().getName().equals(paramName)) { String typeNames = tag.getSingleTypeReference().getName(); result = typeNames.replace(Constants.TYPE_SEPARATOR_CHAR, Constants.DOT); break; } } } return result; } public boolean visit(TypeDeclaration type) throws Exception { if (type instanceof NamespaceDeclaration) { NamespaceDeclaration namespaceDecl = (NamespaceDeclaration) type; fCurrentNamespace = namespaceDecl; fLastUseParts.clear(); if (namespaceDecl.isGlobal()) { return visitGeneral(type); } declarations.push(type); int modifiers = type.getModifiers() | Modifiers.AccNameSpace; fCurrentQualifier = type.getName(); Integer count = fCurrentQualifierCounts.get(fCurrentQualifier); count = count != null ? count + 1 : 1; fCurrentQualifierCounts.put(fCurrentQualifier, count); modifiers = markAsDeprecated(modifiers, type); StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); // $NON-NLS-1$ modifyDeclaration(type, new DeclarationInfo(IModelElement.PACKAGE_DECLARATION, modifiers, type.sourceStart(), type.sourceEnd() - type.sourceStart(), type.getNameStart(), type.getNameEnd() - type.getNameStart(), type.getName(), metadata.toString(), encodeDocInfo(type), null, null)); } else { Declaration parentDeclaration = null; if (!declarations.empty()) { parentDeclaration = declarations.peek(); } declarations.push(type); if (!(parentDeclaration instanceof NamespaceDeclaration)) { type.setModifier(Modifiers.AccGlobal); } // In case we are entering a nested element if (parentDeclaration instanceof MethodDeclaration) { if (fCurrentNamespace == null) { deferredDeclarations.add(type); } else { deferredNamespacedDeclarations.add(type); } return visitGeneral(type); } int modifiers = type.getModifiers(); fCurrentParent = type.getName(); String[] superClasses = processSuperClasses(type); StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); for (int i = 0; i < superClasses.length; ++i) { metadata.append(superClasses[i]); if (i < superClasses.length - 1) { metadata.append(","); //$NON-NLS-1$ } } modifiers = markAsDeprecated(modifiers, type); modifyDeclaration(type, new DeclarationInfo(IModelElement.TYPE, modifiers, type.sourceStart(), type.sourceEnd() - type.sourceStart(), type.getNameStart(), type.getNameEnd() - type.getNameStart(), type.getName(), metadata.toString(), encodeDocInfo(type), fCurrentQualifier, null)); } for (PHPIndexingVisitorExtension visitor : extensions) { visitor.visit(type); } return visitGeneral(type); } protected String[] processSuperClasses(TypeDeclaration type) { ASTListNode superClasses = type.getSuperClasses(); if (superClasses == null) { return new String[] {}; } List<ASTNode> superClassNames = superClasses.getChilds(); List<String> result = new ArrayList<String>(superClassNames.size()); Iterator<ASTNode> iterator = superClassNames.iterator(); while (iterator.hasNext()) { ASTNode nameNode = iterator.next(); String name; if (nameNode instanceof FullyQualifiedReference) { FullyQualifiedReference fullyQualifiedName = (FullyQualifiedReference) nameNode; name = fullyQualifiedName.getFullyQualifiedName(); if (fullyQualifiedName.getNamespace() != null) { String namespace = fullyQualifiedName.getNamespace().getName(); String subnamespace = ""; //$NON-NLS-1$ if (namespace.charAt(0) != NamespaceReference.NAMESPACE_SEPARATOR && namespace.indexOf(NamespaceReference.NAMESPACE_SEPARATOR) > 0) { int firstNSLocation = namespace.indexOf(NamespaceReference.NAMESPACE_SEPARATOR); subnamespace = namespace.substring(firstNSLocation); namespace = namespace.substring(0, firstNSLocation); } if (name.charAt(0) == NamespaceReference.NAMESPACE_SEPARATOR) { name = name.substring(1); } else if (fLastUseParts.containsKey(namespace)) { name = new StringBuilder(fLastUseParts.get(namespace).getNamespace().getFullyQualifiedName()) .append(subnamespace).append(NamespaceReference.NAMESPACE_SEPARATOR) .append(fullyQualifiedName.getName()).toString(); } else if (fCurrentNamespace != null) { name = new StringBuilder(fCurrentNamespace.getName()) .append(NamespaceReference.NAMESPACE_SEPARATOR).append(name).toString(); } } else if (fLastUseParts.containsKey(name)) { name = fLastUseParts.get(name).getNamespace().getFullyQualifiedName(); if (name.charAt(0) == NamespaceReference.NAMESPACE_SEPARATOR) { name = name.substring(1); } } else { if (fCurrentNamespace != null) { name = new StringBuilder(fCurrentNamespace.getName()) .append(NamespaceReference.NAMESPACE_SEPARATOR).append(name).toString(); } } result.add(name); } else if (nameNode instanceof SimpleReference) { result.add(((SimpleReference) nameNode).getName()); } } return (String[]) result.toArray(new String[result.size()]); } /** * Resolve class members that were defined using the @property tag * * @param type * declaration for wich we add the magic variables */ private void resolveMagicMembers(TypeDeclaration type) { if (type instanceof IPHPDocAwareDeclaration) { IPHPDocAwareDeclaration declaration = (IPHPDocAwareDeclaration) type; final PHPDocBlock doc = declaration.getPHPDoc(); if (doc != null) { for (PHPDocTag docTag : doc.getTags()) { final TagKind tagKind = docTag.getTagKind(); if (tagKind == TagKind.PROPERTY || tagKind == TagKind.PROPERTY_READ || tagKind == TagKind.PROPERTY_WRITE) { // http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.property.pkg.html final String[] split = MagicMemberUtil.WHITESPACE_SEPERATOR.split(docTag.getValue().trim()); if (split.length < 2) { break; } String name = removeParenthesis(split); int offset = docTag.sourceStart(); int length = docTag.sourceStart() + 9; Map<String, String> info = new HashMap<String, String>(); info.put("v", split[0]); //$NON-NLS-1$ StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); modifyDeclaration(null, new DeclarationInfo(IModelElement.FIELD, Modifiers.AccPublic, offset, length, offset, length, name, metadata.toString(), encodeDocInfo(info), fCurrentQualifier, fCurrentParent)); } else if (tagKind == TagKind.METHOD) { // http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.method.pkg.html String[] split = MagicMemberUtil.WHITESPACE_SEPERATOR.split(docTag.getValue().trim()); if (split.length < 2) { break; } int methodModifiers = Modifiers.AccPublic; if (Constants.STATIC.equals(split[0].trim())) { if (split.length < 3) { break; } methodModifiers |= Modifiers.AccStatic; split = Arrays.copyOfRange(split, 1, split.length); } String name = removeParenthesis(split); int index = name.indexOf('('); if (index > 0) { name = name.substring(0, index); } int offset = docTag.sourceStart(); int length = docTag.sourceStart() + 6; Map<String, String> info = new HashMap<String, String>(); info.put("r", split[0]); //$NON-NLS-1$ StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); // $NON-NLS-1$ modifyDeclaration(null, new DeclarationInfo(IModelElement.METHOD, methodModifiers, offset, length, offset, length, name, metadata.toString(), encodeDocInfo(info), fCurrentQualifier, fCurrentParent)); } } } } } private String removeParenthesis(final String[] split) { final String name = split[1]; return name.endsWith("()") ? name.substring(0, name.length() - 2) //$NON-NLS-1$ : name; } public boolean visit(UseStatement declaration) throws Exception { Collection<UsePart> parts = declaration.getParts(); for (UsePart part : parts) { String name = null; if (part.getAlias() != null) { name = part.getAlias().getName(); } else { name = part.getNamespace().getName(); int index = name.lastIndexOf(NamespaceReference.NAMESPACE_SEPARATOR); if (index >= 0) { name = name.substring(index + 1); } } fLastUseParts.put(name, part); } return visitGeneral(declaration); } public boolean visit(FieldDeclaration decl) throws Exception { // This is constant declaration: int modifiers = decl.getModifiers(); modifiers = markAsDeprecated(modifiers, decl); modifyDeclaration(decl, new DeclarationInfo(IModelElement.FIELD, modifiers, decl.sourceStart(), decl.sourceEnd() - decl.sourceStart(), decl.getNameStart(), decl.getNameEnd() - decl.getNameStart(), decl.getName(), null, encodeDocInfo(decl), null, null)); return visitGeneral(decl); } public boolean endvisit(FieldDeclaration declaration) throws Exception { endvisitGeneral(declaration); return true; } public boolean visit(PHPFieldDeclaration decl) throws Exception { // This is variable declaration: int modifiers = markAsDeprecated(decl.getModifiers(), decl); StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); modifyDeclaration(decl, new DeclarationInfo(IModelElement.FIELD, modifiers, decl.sourceStart(), decl.sourceEnd() - decl.sourceStart(), decl.getNameStart(), decl.getNameEnd() - decl.getNameStart(), decl.getName(), metadata.toString(), encodeDocInfo(decl), fCurrentQualifier, fCurrentParent)); return visitGeneral(decl); } public boolean endvisit(PHPFieldDeclaration declaration) throws Exception { endvisitGeneral(declaration); return true; } public boolean visit(CallExpression call) throws Exception { FieldDeclaration constantDecl = ASTUtils.getConstantDeclaration(call); if (constantDecl != null) { // In case we are entering a nested element if (!declarations.empty() && declarations.peek() instanceof MethodDeclaration) { deferredDeclarations.add(constantDecl); return visitGeneral(call); } visit((FieldDeclaration) constantDecl); } else { int argsCount = 0; CallArgumentsList args = call.getArgs(); if (args != null && args.getChilds() != null) { argsCount = args.getChilds().size(); } modifyReference(call, new ReferenceInfo(IModelElement.METHOD, call.sourceStart(), call.sourceEnd() - call.sourceStart(), call.getName(), Integer.toString(argsCount), null)); } return visitGeneral(call); } public boolean visit(Include include) throws Exception { // special case for include statements; we need to cache this // information in order to access it quickly: if (include.getExpr() instanceof Scalar) { Scalar filePath = (Scalar) include.getExpr(); modifyReference(include, new ReferenceInfo(IModelElement.METHOD, filePath.sourceStart(), filePath.sourceEnd() - filePath.sourceStart(), "include", Integer //$NON-NLS-1$ .toString(1), null)); String fullPath = ASTUtils.stripQuotes(((Scalar) filePath).getValue()); int idx = Math.max(fullPath.lastIndexOf('/'), fullPath.lastIndexOf('\\')); String lastSegment = fullPath; if (idx != -1) { lastSegment = lastSegment.substring(idx + 1); } StringBuilder metadata = new StringBuilder(); // Fake occurrenceCount, because we do always need one // when metadata != null to make PhpElementResolver#resolve() happy metadata.append(1); metadata.append(QUALIFIER_SEPERATOR); metadata.append(fullPath); modifyDeclaration(include, new DeclarationInfo(IModelElement.IMPORT_DECLARATION, 0, include.sourceStart(), include.sourceEnd() - include.sourceStart(), filePath.sourceStart(), filePath.sourceEnd() - filePath.sourceStart(), lastSegment, metadata.toString(), null, null, null)); } return visitGeneral(include); } public boolean visit(ConstantDeclaration declaration) throws Exception { int accessModifier = declaration.getModifiers() == 0 ? Modifiers.AccPublic : declaration.getModifiers(); int modifiers = Modifiers.AccConstant | Modifiers.AccFinal | accessModifier; if (fCurrentParent != null) { modifiers = modifiers | PHPCoreConstants.AccClassField; } modifiers = markAsDeprecated(modifiers, declaration); ConstantReference constantName = declaration.getConstantName(); int offset = constantName.sourceStart(); int length = constantName.sourceEnd(); StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); modifyDeclaration(declaration, new DeclarationInfo(IModelElement.FIELD, modifiers, offset, length, offset, length, ASTUtils.stripQuotes(constantName.getName()), metadata.toString(), encodeDocInfo(declaration), fCurrentQualifier, fCurrentParent)); return visitGeneral(declaration); } public boolean endvisit(ConstantDeclaration declaration) throws Exception { endvisitGeneral(declaration); return true; } public boolean visit(Assignment assignment) throws Exception { Expression left = assignment.getVariable(); if (left instanceof FieldAccess) { // class variable ($this->a = .) FieldAccess fieldAccess = (FieldAccess) left; Expression dispatcher = fieldAccess.getDispatcher(); if (dispatcher instanceof VariableReference && "$this".equals(((VariableReference) dispatcher).getName())) { //$NON-NLS-1$ Expression field = fieldAccess.getField(); if (field instanceof SimpleReference) { SimpleReference var = (SimpleReference) field; int modifiers = Modifiers.AccPublic; int offset = var.sourceStart(); int length = var.sourceEnd() - offset; StringBuilder metadata = new StringBuilder(); metadata.append(fCurrentQualifier != null ? fCurrentQualifierCounts.get(fCurrentQualifier) : 1); metadata.append(QUALIFIER_SEPERATOR); modifyDeclaration(assignment, new DeclarationInfo(IModelElement.FIELD, modifiers, offset, length, offset, length, '$' + var.getName(), metadata.toString(), null, fCurrentQualifier, fCurrentParent)); } } } else if (left instanceof VariableReference) { int modifiers = Modifiers.AccPublic | Modifiers.AccGlobal; String variableName = ((VariableReference) left).getName(); if (!declarations.empty() && declarations.peek() instanceof MethodDeclaration && !methodGlobalVars.peek().contains(variableName)) { return visitGeneral(assignment); } int offset = left.sourceStart(); int length = left.sourceEnd() - offset; if (!globalScopeVars.contains(variableName)) { globalScopeVars.add(variableName); modifyDeclaration(assignment, new DeclarationInfo(IModelElement.FIELD, modifiers, offset, length, offset, length, ((VariableReference) left).getName(), null, null, null, null)); } } return visitGeneral(assignment); } public boolean endvisit(Assignment assignment) throws Exception { endvisitGeneral(assignment); return true; } public boolean visit(GlobalStatement s) throws Exception { if (!declarations.empty() && declarations.peek() instanceof MethodDeclaration) { for (Expression var : s.getVariables()) { if (var instanceof ReferenceExpression) { var = ((ReferenceExpression) var).getVariable(); } if (var instanceof SimpleReference) { methodGlobalVars.peek().add(((SimpleReference) var).getName()); } } } return visitGeneral(s); } public boolean visit(TypeReference reference) throws Exception { modifyReference(reference, new ReferenceInfo(IModelElement.TYPE, reference.sourceStart(), reference.sourceEnd() - reference.sourceStart(), reference.getName(), null, null)); return visitGeneral(reference); } public boolean visit(Statement node) throws Exception { if (node instanceof PHPFieldDeclaration) { return visit((PHPFieldDeclaration) node); } if (node instanceof FieldDeclaration) { return visit((FieldDeclaration) node); } if (node instanceof ConstantDeclaration) { return visit((ConstantDeclaration) node); } if (node instanceof GlobalStatement) { return visit((GlobalStatement) node); } if (node instanceof UseStatement) { return visit((UseStatement) node); } for (PHPIndexingVisitorExtension visitor : extensions) { visitor.visit(node); } return visitGeneral(node); } public boolean endvisit(Statement node) throws Exception { if (node instanceof PHPFieldDeclaration) { return endvisit((PHPFieldDeclaration) node); } if (node instanceof FieldDeclaration) { return endvisit((FieldDeclaration) node); } if (node instanceof ConstantDeclaration) { return endvisit((ConstantDeclaration) node); } for (PHPIndexingVisitorExtension visitor : extensions) { visitor.endvisit(node); } endvisitGeneral(node); return true; } public boolean visit(Expression node) throws Exception { if (node instanceof Assignment) { return visit((Assignment) node); } if (node instanceof TypeReference) { return visit((TypeReference) node); } if (node instanceof Include) { return visit((Include) node); } if (node instanceof PHPCallExpression) { return visit((PHPCallExpression) node); } if (node instanceof FieldAccess) { return visit((FieldAccess) node); } if (node instanceof StaticConstantAccess) { return visit((StaticConstantAccess) node); } for (PHPIndexingVisitorExtension visitor : extensions) { visitor.visit(node); } return visitGeneral(node); } public boolean endvisit(Expression node) throws Exception { if (node instanceof Assignment) { return endvisit((Assignment) node); } for (PHPIndexingVisitorExtension visitor : extensions) { visitor.endvisit(node); } endvisitGeneral(node); return true; } public boolean endvisit(ModuleDeclaration declaration) throws Exception { while (deferredDeclarations != null && !deferredDeclarations.isEmpty()) { final ASTNode[] declarations = deferredDeclarations.toArray(new ASTNode[deferredDeclarations.size()]); deferredDeclarations.clear(); for (ASTNode deferred : declarations) { deferred.traverse(this); } } for (PHPIndexingVisitorExtension visitor : extensions) { visitor.endvisit(declaration); } fLastUseParts.clear(); globalScopeVars.clear(); endvisitGeneral(declaration); return true; } public void endvisitGeneral(ASTNode node) throws Exception { fNodes.pop(); } public boolean visitGeneral(ASTNode node) throws Exception { fNodes.push(node); return true; } public boolean endvisit(FieldAccess declaration) throws Exception { endvisitGeneral(declaration); return true; } public boolean visit(FieldAccess access) throws Exception { // This is variable field access: if (access.getField() instanceof SimpleReference) { SimpleReference simpleReference = (SimpleReference) access.getField(); String name = simpleReference.getName(); if (!name.startsWith(DOLLAR)) { name = DOLLAR + name; } modifyReference(access, new ReferenceInfo(IModelElement.FIELD, simpleReference.sourceStart(), simpleReference.sourceEnd() - simpleReference.sourceStart(), name, null, null)); } return visitGeneral(access); } public boolean visit(StaticConstantAccess access) throws Exception { // This is constant field access: if (access.getConstant() != null) { final ConstantReference constantReference = access.getConstant(); final String name = constantReference.getName(); modifyReference(access, new ReferenceInfo(IModelElement.FIELD, constantReference.sourceStart(), constantReference.sourceEnd() - constantReference.sourceStart(), name, null, null)); } return visitGeneral(access); } }