wxString PHPExpression::DoSimplifyExpression(int depth, PHPSourceFile::Ptr_t sourceFile) { if(depth > 5 || sourceFile == NULL) { // avoid infinite recursion, by limiting the nest level to 5 return ""; } // Use the provided sourceFile if 'm_sourceFile' is NULL if(!m_sourceFile) { m_sourceFile = sourceFile; } // Parse the input source file PHPEntityBase::Ptr_t scope = sourceFile->CurrentScope(); const PHPEntityBase* innerClass = sourceFile->Class(); // Check the first token // Phase 1: // Loop over the expression and resolve as much as we can here for the first token // (mainly, replace $this, self, static, $variable) phpLexerToken token; wxString newExpr; wxString firstToken; int firstTokenType = wxNOT_FOUND; for(size_t i = 0; i < m_expression.size(); ++i) { token = m_expression.at(i); if(i == 0) { // Perform basic replacements that we can conduct here without the need of the global // lookup table if(token.type == kPHP_T_PARENT) { if(!innerClass) return ""; firstToken = innerClass->GetFullName(); firstTokenType = kPHP_T_PARENT; } else if(token.type == kPHP_T_THIS) { // the first token is $this // replace it with the current class absolute path if(!innerClass) return ""; firstToken = innerClass->GetFullName(); // Is always in absolute path } else if(token.type == kPHP_T_SELF) { // Same as $this: replace it with the current class absolute path if(!innerClass) return ""; firstToken = innerClass->GetFullName(); // Is always in absolute path firstTokenType = kPHP_T_SELF; } else if(token.type == kPHP_T_STATIC) { // Same as $this: replace it with the current class absolute path if(!innerClass) return ""; firstToken = innerClass->GetFullName(); // Is always in absolute path firstTokenType = kPHP_T_STATIC; } else if(token.type == kPHP_T_VARIABLE) { // the expression being evaluated starts with a variable (e.g. $a->something()->) // in this case, use the current scope ('scope') and replace it with the real type // Note that the type can be another expression // e.g.: // $a = $this->getQuery(); // $b = $a->fetchAll()-> // so if the expression being evaluated is "$a->fetchAll()->" // we first replace $a with '$this->getQuery()' so it becomes: // $this->getQuery()->fetchAll()-> // However, $this also need a replacement so eventually, it becomes like this: // \MyClass->getQuery->fetchAll-> and this is something that we can evaluate easily using // our lookup tables (note that the parenthessis are missing on purpose) PHPEntityBase::Ptr_t local = scope->FindChild(token.text); if(local && local->Cast<PHPEntityVariable>()) { if(!local->Cast<PHPEntityVariable>()->GetTypeHint().IsEmpty()) { // we have type hint! - use it firstToken = local->Cast<PHPEntityVariable>()->GetTypeHint(); } else if(!local->Cast<PHPEntityVariable>()->GetExpressionHint().IsEmpty()) { // we have an expression hint - use it // append the "->" to the expression to make sure that the parser will understand it // as an expression PHPExpression e(m_text, local->Cast<PHPEntityVariable>()->GetExpressionHint() + "->"); firstToken = e.DoSimplifyExpression(depth + 1, m_sourceFile); if(firstToken.EndsWith("->")) { // remove the last 2 characters firstToken.RemoveLast(2); } } } else { // this local variable does not exist in the current scope // This is probably a word-completion for local variable m_filter = token.text; return ""; } } else if(token.type == kPHP_T_IDENTIFIER) { // an identifier, convert it to the fullpath firstToken = sourceFile->MakeIdentifierAbsolute(token.text); } } if(!firstToken.IsEmpty()) { newExpr = firstToken; firstToken.Clear(); } else { newExpr << " " << token.text; } } // Phase 2: // Now break the tokens from the lexers into something that we can work with // when using the lookup table. // The split in this phase is done by searching for kPHP_T_OBJECT_OPERATOR and kPHP_T_PAAMAYIM_NEKUDOTAYIM // separators m_expression = CreateExpression("<?php " + newExpr); Part part; wxString currentText; for(size_t i = 0; i < m_expression.size(); ++i) { token = m_expression.at(i); // Remove any braces and split by object kPHP_T_OBJECT_OPERATOR and kPHP_T_PAAMAYIM_NEKUDOTAYIM switch(token.type) { case kPHP_T_OPEN_TAG: break; case '(': if(!currentText.IsEmpty()) { part.m_text = currentText; } break; case ')': // skip it break; case kPHP_T_PAAMAYIM_NEKUDOTAYIM: case kPHP_T_OBJECT_OPERATOR: if(!currentText.IsEmpty() && part.m_text.IsEmpty()) { if(m_parts.empty() && token.type == kPHP_T_PAAMAYIM_NEKUDOTAYIM) { // The first token in the "parts" list has a scope resolving operator ("::") // we need to make sure that the indetifier is provided in fullpath part.m_text = sourceFile->MakeIdentifierAbsolute(currentText); } else { part.m_text = currentText; } } if(m_parts.empty()) { // If the first token before the simplication was 'parent' // keyword, we need to carry this over part.m_textType = firstTokenType; } part.m_operator = token.type; part.m_operatorText = token.text; m_parts.push_back(part); // cleanup currentText.clear(); part.m_text.clear(); part.m_operatorText.clear(); part.m_operator = wxNOT_FOUND; break; case kPHP_T_THIS: case kPHP_T_SELF: case kPHP_T_STATIC: part.m_textType = token.type; currentText << token.text; break; default: currentText << token.text; break; } } if(!currentText.IsEmpty()) { m_filter = currentText; } wxString simplified; List_t::iterator iter = m_parts.begin(); for(; iter != m_parts.end(); ++iter) { simplified << iter->m_text << iter->m_operatorText; } return simplified.Trim().Trim(false); }
void PHPDocVisitor::OnEntity(PHPEntityBase::Ptr_t entity) { // Locate a comment for this entity entity->SetFilename(m_sourceFile.GetFilename()); if(!entity->GetDocComment().IsEmpty()) { // PHPDoc was already assigned to this entity during the parsing phase if(entity->Is(kEntityTypeClass)) { // Process @property tags here PHPDocComment docComment(m_sourceFile, entity->GetDocComment()); if(!docComment.GetProperties().empty()) { // Got some @properties std::for_each(docComment.GetProperties().begin(), docComment.GetProperties().end(), [&](PHPDocComment::Property::Map_t::value_type& p) { PHPEntityBase::Ptr_t child = entity->FindChild(p.second.name); if(!child) { // No child of this type, create a new property and add it child.Reset(new PHPEntityVariable()); child->SetFilename(m_sourceFile.GetFilename()); child->SetLine(entity->GetLine()); child->Cast<PHPEntityVariable>()->SetTypeHint(p.second.type); child->Cast<PHPEntityVariable>()->SetFlag(kVar_Member); // Member variable child->Cast<PHPEntityVariable>()->SetFlag(kVar_Public); // Public access child->SetShortName(p.second.name); child->SetFullName(p.second.name); entity->AddChild(child); } }); } else if(!docComment.GetMethods().empty()) { std::for_each(docComment.GetMethods().begin(), docComment.GetMethods().end(), [&](PHPEntityBase::Ptr_t method) { entity->AddChild(method); }); } } } else { // search for the comment placed at the top of the variable // this is why we use here -1 int lineNum = (entity->GetLine() - 1); // for debugging purposes wxString entityName = entity->GetShortName(); wxUnusedVar(entityName); std::map<int, phpLexerToken>::iterator iter = m_comments.find(lineNum); if(iter == m_comments.end()) { // try to locate a comment on the same line ++lineNum; iter = m_comments.find(lineNum); } if(iter != m_comments.end()) { // we got a match entity->SetDocComment(iter->second.Text()); m_comments.erase(iter); PHPDocComment docComment(m_sourceFile, entity->GetDocComment()); if(entity->Is(kEntityTypeFunction) && !docComment.GetReturn().IsEmpty()) { entity->Cast<PHPEntityFunction>()->SetReturnValue(docComment.GetReturn()); } else if(entity->Is(kEntityTypeVariable) && !entity->Cast<PHPEntityVariable>()->IsFunctionArg()) { // A global variable, const or a member entity->Cast<PHPEntityVariable>()->SetTypeHint(docComment.GetVar()); } } else if(entity->Is(kEntityTypeVariable) && entity->Parent() && entity->Parent()->Is(kEntityTypeFunction) && entity->Cast<PHPEntityVariable>()->IsFunctionArg()) { // A function argument PHPDocComment docComment(m_sourceFile, entity->Parent()->GetDocComment()); wxString typeHint = docComment.GetParam(entity->GetFullName()); if(!typeHint.IsEmpty()) { entity->Cast<PHPEntityVariable>()->SetTypeHint(typeHint); } } } }