File indexing completed on 2024-04-21 15:24:07

0001 /*
0002     SPDX-FileCopyrightText: 2012 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "phpdocsplugin.h"
0008 #include "phpdocsmodel.h"
0009 
0010 #include <KPluginFactory>
0011 #include <KAboutData>
0012 #include <ksettings/Dispatcher>
0013 
0014 #include <interfaces/idocumentation.h>
0015 #include <interfaces/icore.h>
0016 #include <interfaces/idocumentationcontroller.h>
0017 
0018 #include <language/duchain/duchain.h>
0019 #include <language/duchain/declaration.h>
0020 #include <language/duchain/duchainlock.h>
0021 
0022 #include <language/duchain/classdeclaration.h>
0023 #include <language/duchain/functiondeclaration.h>
0024 #include <language/duchain/classmemberdeclaration.h>
0025 #include <language/duchain/classfunctiondeclaration.h>
0026 
0027 #include <QFile>
0028 
0029 #include "phpdocsdebug.h"
0030 #include "phpdocumentation.h"
0031 #include "phpdocssettings.h"
0032 
0033 using namespace KDevelop;
0034 
0035 K_PLUGIN_FACTORY_WITH_JSON(PhpDocsFactory, "kdevphpdocs.json", registerPlugin<PhpDocsPlugin>();)
0036 
0037 PhpDocsPlugin::PhpDocsPlugin(QObject* parent, const QVariantList& args)
0038     : IPlugin(QStringLiteral("kdevphpdocs"), parent)
0039     , m_model(new PhpDocsModel(this))
0040 {
0041     Q_UNUSED(args);
0042 
0043     readConfig();
0044 
0045     KSettings::Dispatcher::registerComponent( QStringLiteral("kdevphpdocs_config"), this, "readConfig" );
0046 }
0047 
0048 PhpDocsPlugin::~PhpDocsPlugin()
0049 {
0050 }
0051 
0052 QString PhpDocsPlugin::name() const
0053 {
0054     return QStringLiteral("PHP");
0055 }
0056 
0057 QIcon PhpDocsPlugin::icon() const
0058 {
0059     static QIcon icon = QIcon::fromTheme(QStringLiteral("application-x-php"));
0060     return icon;
0061 }
0062 
0063 void PhpDocsPlugin::readConfig()
0064 {
0065     // since PhpDocsSettings pointer in this plugin is distinct from the one in the KCM
0066     // we have to trigger reading manually
0067     PhpDocsSettings::self()->load();
0068 }
0069 
0070 ///TODO: possibly return multiple filenames (i.e. fallbacks) when doing local lookups
0071 QString PhpDocsPlugin::getDocumentationFilename( KDevelop::Declaration* dec, const bool& isLocal ) const
0072 {
0073     QString fname;
0074 
0075     //TODO: most of the SPL stuff is not found for me in the deb package php-doc
0076     //      => check newer documentation or give a fallback to ref.spl.html
0077     if ( ClassFunctionDeclaration* fdec = dynamic_cast<ClassFunctionDeclaration*>( dec ) ) {
0078         // class methods -> php.net/CLASS.METHOD
0079         // local: either CLASS.METHOD.html or function.CLASS-METHOD.html... really sucks :-/
0080         //        for now, use the latter...
0081         if ( dec->context() && dec->context()->type() == DUContext::Class && dec->context()->owner() ) {
0082             QString className = dec->context()->owner()->identifier().toString();
0083 
0084             if ( !isLocal ) {
0085                 fname = className + '.' + fdec->identifier().toString();
0086             } else {
0087                 if ( fdec->isConstructor() ) {
0088                     fname = QStringLiteral("construct");
0089                 } else if ( fdec->isDestructor() ) {
0090                     fname = QStringLiteral("destruct");
0091                 } else {
0092                     fname = fdec->identifier().toString();
0093                 }
0094                 //TODO: CLASS.METHOD.html e.g. for xmlreader etc. pp.
0095                 fname = "function." + className + '-' + fname;
0096             }
0097         }
0098     } else if ( dynamic_cast<ClassDeclaration*>(dec) ) {
0099         fname = "class." + dec->identifier().toString();
0100     } else if ( dynamic_cast<FunctionDeclaration*>(dec) ) {
0101         fname = "function." + dec->identifier().toString();
0102     }
0103     // check for superglobals / reserved variables
0104     else if ( dec->identifier() == Identifier(QStringLiteral("GLOBALS")) ||
0105                 dec->identifier() == Identifier(QStringLiteral("php_errormsg")) ||
0106                 dec->identifier() == Identifier(QStringLiteral("HTTP_RAW_POST_DATA")) ||
0107                 dec->identifier() == Identifier(QStringLiteral("http_response_header")) ||
0108                 dec->identifier() == Identifier(QStringLiteral("argc")) ||
0109                 dec->identifier() == Identifier(QStringLiteral("argv")) ||
0110                 dec->identifier() == Identifier(QStringLiteral("_GET")) ||
0111                 dec->identifier() == Identifier(QStringLiteral("_POST")) ||
0112                 dec->identifier() == Identifier(QStringLiteral("_FILES")) ||
0113                 dec->identifier() == Identifier(QStringLiteral("_REQUEST")) ||
0114                 dec->identifier() == Identifier(QStringLiteral("_SESSION")) ||
0115                 dec->identifier() == Identifier(QStringLiteral("_ENV")) ||
0116                 dec->identifier() == Identifier(QStringLiteral("_COOKIE")) ) {
0117         if ( isLocal ) {
0118             fname = QStringLiteral("reserved.variables.") + dec->identifier().toString().remove('_');
0119         } else {
0120             fname = dec->identifier().toString();
0121         }
0122     }
0123 
0124     qCDebug(DOCS) << fname;
0125 
0126     if ( !fname.isEmpty() && isLocal ) {
0127         fname = fname.toLower();
0128         fname.replace('_', '-');
0129         fname.append(".html");
0130     }
0131 
0132     return fname;
0133 }
0134 
0135 IDocumentation::Ptr PhpDocsPlugin::documentationForDeclaration( Declaration* dec ) const
0136 {
0137     if ( dec ) {
0138         DUChainReadLocker lock( DUChain::lock() );
0139 
0140         // filter non global or non-php declarations
0141         if ( dec->topContext()->url() != m_model->internalFunctionFile() ) {
0142             return {};
0143         }
0144 
0145         QUrl url = PhpDocsSettings::phpDocLocation();
0146         qCDebug(DOCS) << url;
0147 
0148         QString file = getDocumentationFilename( dec, url.isLocalFile() );
0149         if ( file.isEmpty() ) {
0150             qCDebug(DOCS) << "no documentation pattern found for" << dec->toString();
0151             return {};
0152         }
0153 
0154         url.setPath( url.path() + '/' + file);
0155         if ( url.isLocalFile() && !QFile::exists( url.toLocalFile() ) ) {
0156             qCDebug(DOCS) << "bad path" << url << "for documentation of" << dec->toString() << " - aborting";
0157             return {};
0158         }
0159 
0160         qCDebug(DOCS) << "php documentation located at " << url << "for" << dec->toString();
0161         return documentationForUrl(url, dec->qualifiedIdentifier().toString());
0162     }
0163 
0164     return {};
0165 }
0166 
0167 IDocumentation::Ptr PhpDocsPlugin::documentation(const QUrl& url) const
0168 {
0169     if (url.toString().startsWith(PhpDocsSettings::phpDocLocation().toString())) {
0170         return documentationForUrl(url, QString());
0171     }
0172     return {};
0173 }
0174 
0175 
0176 QAbstractListModel* PhpDocsPlugin::indexModel() const
0177 {
0178     return m_model;
0179 }
0180 
0181 IDocumentation::Ptr PhpDocsPlugin::documentationForIndex(const QModelIndex& index) const
0182 {
0183     return documentationForDeclaration(static_cast<Declaration*>(
0184         index.data(PhpDocsModel::DeclarationRole).value<DeclarationPointer>().data()
0185     ));
0186 }
0187 
0188 void PhpDocsPlugin::loadUrl(const QUrl& url) const
0189 {
0190     qCDebug(DOCS) << "loading URL" << url;
0191     auto doc = documentationForUrl(url, QString());
0192     ICore::self()->documentationController()->showDocumentation(doc);
0193 }
0194 
0195 void PhpDocsPlugin::showDocumentation(const QUrl& url)
0196 {
0197     auto doc = documentationForUrl(url, url.toString());
0198     ICore::self()->documentationController()->showDocumentation(doc);
0199 }
0200 
0201 IDocumentation::Ptr PhpDocsPlugin::documentationForUrl(const QUrl& url, const QString& name, const QByteArray& description) const
0202 {
0203     return IDocumentation::Ptr(new PhpDocumentation( url, name, description, const_cast<PhpDocsPlugin*>(this)));
0204 }
0205 
0206 IDocumentation::Ptr PhpDocsPlugin::homePage() const
0207 {
0208     QUrl url = PhpDocsSettings::phpDocLocation();
0209     if ( url.isLocalFile() ) {
0210         url.setPath(url.path() + "/index.html");
0211     } else {
0212         url.setPath(url.path() + "/manual");
0213     }
0214     return documentationForUrl(url, i18n("PHP Documentation"));
0215 }
0216 
0217 #include "phpdocsplugin.moc"
0218 
0219 #include "moc_phpdocsplugin.cpp"