/******************************************************************************* * Copyright (c) 2015 Zend Technologies 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: * Zend Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.debug.core.zend.debugger; import static org.eclipse.php.internal.debug.core.model.IPHPDataType.DataType.PHP_VIRTUAL_CLASS; import static org.eclipse.php.internal.debug.core.model.IVariableFacet.Facet.KIND_CONSTANT; import static org.eclipse.php.internal.debug.core.model.IVariableFacet.Facet.KIND_OBJECT_MEMBER; import static org.eclipse.php.internal.debug.core.model.IVariableFacet.Facet.MOD_STATIC; import static org.eclipse.php.internal.debug.core.model.IVariableFacet.Facet.VIRTUAL_CLASS; import java.text.MessageFormat; import java.util.Map; import java.util.WeakHashMap; import org.eclipse.php.internal.debug.core.model.IPHPDataType.DataType; import org.eclipse.php.internal.debug.core.model.IVariableFacet.Facet; import org.eclipse.php.internal.debug.core.model.VariablesUtil; /** * Utility class for Zend Debugger expressions. * * @author Bartlomiej Laczkowski */ public class ExpressionsUtil { private static final String CHECK_CLASS_METHOD_EXISTS = "eval(''if (class_exists(\\''ReflectionClass\\'')) return (new ReflectionClass({0}))->hasMethod(\\''{1}\\''); else return false;'');"; //$NON-NLS-1$ private static final class FetchStaticsExpression extends DefaultExpression { static final String FETCH_STATIC_MEMBERS = "eval(''if (class_exists(\\''ReflectionClass\\'') && class_exists(\\''{0}\\'')) return (new ReflectionClass(\\''{0}\\''))->getStaticProperties(); else return array();'');"; //$NON-NLS-1$ public FetchStaticsExpression(String className) { super(MessageFormat.format(FETCH_STATIC_MEMBERS, className), className); } @Override public Expression createChildExpression(String endName, String endRepresentation, Facet... facets) { return new DefaultExpression(this, endName, "::$" + endName, KIND_OBJECT_MEMBER, MOD_STATIC); //$NON-NLS-1$ } } private static final class FetchClassConstantsExpression extends DefaultExpression { static final String FETCH_CONSTANTS = "eval(''if (class_exists(\\''ReflectionClass\\'') && class_exists(\\''{0}\\'')) return (new ReflectionClass(\\''{0}\\''))->getConstants(); else return array();'');"; //$NON-NLS-1$ public FetchClassConstantsExpression(String className) { super(MessageFormat.format(FETCH_CONSTANTS, className), className); } @Override public Expression createChildExpression(String endName, String endRepresentation, Facet... facets) { return new DefaultExpression(this, endName, "::" + endName, KIND_CONSTANT); //$NON-NLS-1$ } } private static final class FetchConstantExpression extends DefaultExpression { static final String FETCH_CONSTANT = "eval(''return constant(\\''{0}\\'');'');"; //$NON-NLS-1$ public FetchConstantExpression(String constantName) { super(MessageFormat.format(FETCH_CONSTANT, constantName), constantName); } } private static final class FetchStaticsVisibilityExpression extends DefaultExpression { static final String FETCH_STATICS_MODIFIERS = "eval(''if (class_exists(\\''ReflectionProperty\\'')) return array({0}); else return array();'');"; //$NON-NLS-1$ static final String TUPLE_ELEMENT = "(new ReflectionProperty(\\''{0}\\'', \\''{1}\\''))->getModifiers()"; //$NON-NLS-1$ public FetchStaticsVisibilityExpression(String tuple) { super(MessageFormat.format(FETCH_STATICS_MODIFIERS, tuple)); } } private static final int PROP_MOD_PUBLIC = 1 << 8; private static final int PROP_MOD_PROTECTED = 1 << 9; private static final int PROP_MOD_PRIVATE = 1 << 10; private static final Map<Expression, String> staticMemberClassNames = new WeakHashMap<Expression, String>(); private static Map<ExpressionsManager, ExpressionsUtil> fInstance = new WeakHashMap<>(); private ExpressionsManager fExpressionsManager; /** * */ private ExpressionsUtil(ExpressionsManager expressionsManager) { fExpressionsManager = expressionsManager; } public static ExpressionsUtil getInstance(ExpressionsManager expressionsManager) { if (fInstance.get(expressionsManager) == null) { fInstance.put(expressionsManager, new ExpressionsUtil(expressionsManager)); } return fInstance.get(expressionsManager); } /** * Returns expressions of static members for given class. * * @param className * @param expressionsManager * @return expressions of static members for given class */ public Expression[] fetchStaticMembers(String className) { Expression staticMembers = new FetchStaticsExpression(className); fExpressionsManager.update(staticMembers, 1); Expression[] members = staticMembers.getValue().getChildren(); // Possibly interrupted, crash, etc. if (members == null) return new Expression[0]; int[] mods = fetchStaticMembersVisibility(className, members); // Possibly interrupted, crash, etc. if (mods == null) return new Expression[0]; if (members.length > 0) { for (int i = 0; i < members.length; i++) { Expression member = members[i]; staticMemberClassNames.put(member, className); if ((mods[i] & PROP_MOD_PRIVATE) > 0) member.addFacets(Facet.MOD_PRIVATE); else if ((mods[i] & PROP_MOD_PROTECTED) > 0) member.addFacets(Facet.MOD_PROTECTED); else if ((mods[i] & PROP_MOD_PUBLIC) > 0) member.addFacets(Facet.MOD_PUBLIC); } return members; } return new Expression[0]; } public Expression[] fetchClassConstants(String className) { Expression constants = new FetchClassConstantsExpression(className); fExpressionsManager.update(constants, 1); Expression[] members = constants.getValue().getChildren(); if (members == null) return new Expression[0]; for (Expression e : members) { e.addFacets(Facet.MOD_PUBLIC); } return members; } public Expression fetchConstant(String constantName) { Expression constant = new FetchConstantExpression(constantName); fExpressionsManager.update(constant, 1); Expression newExpression = new DefaultExpression(constantName, KIND_CONSTANT, Facet.MOD_PUBLIC); newExpression.setValue(constant.getValue()); return newExpression; } private boolean invokeMethod(String object, String method, StringBuffer result) { Expression e = new DefaultExpression(MessageFormat.format(CHECK_CLASS_METHOD_EXISTS, object, method)); fExpressionsManager.update(e, 1); if (e.getValue().getValue() != null && e.getValue().getValue().equals("1")) { String expression = object + "->" + method + "()"; //$NON-NLS-1$ //$NON-NLS-2$ e = new DefaultExpression(expression); fExpressionsManager.getExpressionValue(e, 1); fExpressionsManager.update(e, 1); result.append(e.getValue().getValue()); return true; } return false; } /** * Return class name for corresponding static member expression. * * @param staticMember * @return class name for corresponding static member expression */ public static String fetchStaticMemberClassName(Expression staticMember) { return staticMemberClassNames.get(staticMember); } /** * Returns "virtual class" expression with child static members. * * @param className * @param expressionsManager * @return "virtual class" expression with child static members */ public Expression fetchStaticContext(String className) { Expression[] staticMembers = fetchStaticMembers(className); if (staticMembers.length == 0) return null; Expression classStaticContext = new DefaultExpression(VariablesUtil.CLASS_INDICATOR, VIRTUAL_CLASS); ExpressionValue classStaticContextValue = new ExpressionValue(PHP_VIRTUAL_CLASS, className, "Class of: " //$NON-NLS-1$ + className, staticMembers, staticMembers.length); classStaticContext.setValue(classStaticContextValue); return classStaticContext; } private int[] fetchStaticMembersVisibility(String className, Expression[] members) { StringBuilder tuple = new StringBuilder(); for (int i = 0; i < members.length; i++) { tuple.append(MessageFormat.format(FetchStaticsVisibilityExpression.TUPLE_ELEMENT, className, members[i].getLastName())); if (i < members.length - 1) tuple.append(','); } Expression fetchModifiersExpression = new FetchStaticsVisibilityExpression(tuple.toString()); fExpressionsManager.update(fetchModifiersExpression, 1); Expression[] computed = fetchModifiersExpression.getValue().getOriChildren(); if (computed == null) return null; int[] mods = new int[computed.length]; for (int i = 0; i < computed.length; i++) mods[i] = Integer.valueOf((String) computed[i].getValue().getValue()); return mods; } public Expression fetchClassContext(String className) { Expression classContext = new DefaultExpression(VariablesUtil.CLASS_INDICATOR, VIRTUAL_CLASS); ExpressionValue classStaticContextValue = new ExpressionValue(PHP_VIRTUAL_CLASS, className, className, null); classContext.setValue(classStaticContextValue); return classContext; } public String getValueDetail(Expression expression) { ExpressionValue value = expression.getValue(); if (value.getDataType() == DataType.PHP_OBJECT) { StringBuffer result = new StringBuffer(); boolean exists = invokeMethod(expression.getFullName(), "__toString", result); //$NON-NLS-1$ if (exists) return result.toString(); } else if (value.getDataType() == DataType.PHP_ARRAY) { fExpressionsManager.update(expression, 1); StringBuffer result = new StringBuffer("["); //$NON-NLS-1$ for (int i = 0; i < expression.getValue().getChildren().length; i++) { Expression child = expression.getValue().getChildren()[i]; if (i > 0) { result.append(","); //$NON-NLS-1$ result.append(" "); //$NON-NLS-1$ } result.append(child.getLastName()); result.append(" => "); //$NON-NLS-1$ result.append(getValueDetail(child)); } result.append("]"); //$NON-NLS-1$ return result.toString(); } else if (value.getDataType() == DataType.PHP_STRING) { String result = expression.getValue().getValueAsString(); int length = result.length(); return result.substring(1, length - 1); } return expression.getValue().getValueAsString(); } /** * Returns the variable value. * * @param variable * The variable name * @return */ public Expression buildExpression(String variable) { Expression expression = fExpressionsManager.buildExpression(variable); fExpressionsManager.getExpressionValue(expression, 1); fExpressionsManager.update(expression, 1); return expression; } }