Repository: easysoft/zentaophp Branch: master Commit: f1ce398f0a11 Files: 234 Total size: 1.4 MB Directory structure: gitextract_zm9qj3qj/ ├── .gitignore ├── .htaccess ├── Makefile ├── VERSION ├── config/ │ ├── config.php │ ├── filter.php │ └── my.example.php ├── db/ │ └── blog.sql ├── framework/ │ ├── base/ │ │ ├── control.class.php │ │ ├── helper.class.php │ │ ├── model.class.php │ │ └── router.class.php │ ├── control.class.php │ ├── helper.class.php │ ├── model.class.php │ ├── router.class.php │ └── tests/ │ └── helper/ │ ├── .case001.expect │ ├── .case002.expect │ ├── .case003.expect │ ├── .case004.expect │ ├── case001.php │ ├── case002.php │ ├── case003.php │ ├── case004.php │ ├── import1.php │ └── import2.php ├── index.php ├── js/ │ └── my.js ├── lib/ │ ├── base/ │ │ ├── dao/ │ │ │ └── dao.class.php │ │ ├── filter/ │ │ │ └── filter.class.php │ │ ├── front/ │ │ │ └── front.class.php │ │ └── pager/ │ │ └── pager.class.php │ ├── dao/ │ │ └── dao.class.php │ ├── filter/ │ │ └── filter.class.php │ ├── front/ │ │ └── front.class.php │ ├── mobile/ │ │ └── mobile.class.php │ ├── pager/ │ │ └── pager.class.php │ └── purifier/ │ ├── purifier.class.php │ └── standalone/ │ └── HTMLPurifier/ │ ├── ConfigSchema/ │ │ ├── Builder/ │ │ │ ├── ConfigSchema.php │ │ │ └── Xml.php │ │ ├── Exception.php │ │ ├── Interchange/ │ │ │ ├── Directive.php │ │ │ └── Id.php │ │ ├── Interchange.php │ │ ├── InterchangeBuilder.php │ │ ├── Validator.php │ │ ├── ValidatorAtom.php │ │ ├── schema/ │ │ │ ├── Attr.AllowedClasses.txt │ │ │ ├── Attr.AllowedFrameTargets.txt │ │ │ ├── Attr.AllowedRel.txt │ │ │ ├── Attr.AllowedRev.txt │ │ │ ├── Attr.ClassUseCDATA.txt │ │ │ ├── Attr.DefaultImageAlt.txt │ │ │ ├── Attr.DefaultInvalidImage.txt │ │ │ ├── Attr.DefaultInvalidImageAlt.txt │ │ │ ├── Attr.DefaultTextDir.txt │ │ │ ├── Attr.EnableID.txt │ │ │ ├── Attr.ForbiddenClasses.txt │ │ │ ├── Attr.IDBlacklist.txt │ │ │ ├── Attr.IDBlacklistRegexp.txt │ │ │ ├── Attr.IDPrefix.txt │ │ │ ├── Attr.IDPrefixLocal.txt │ │ │ ├── AutoFormat.AutoParagraph.txt │ │ │ ├── AutoFormat.Custom.txt │ │ │ ├── AutoFormat.DisplayLinkURI.txt │ │ │ ├── AutoFormat.Linkify.txt │ │ │ ├── AutoFormat.PurifierLinkify.DocURL.txt │ │ │ ├── AutoFormat.PurifierLinkify.txt │ │ │ ├── AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt │ │ │ ├── AutoFormat.RemoveEmpty.RemoveNbsp.txt │ │ │ ├── AutoFormat.RemoveEmpty.txt │ │ │ ├── AutoFormat.RemoveSpansWithoutAttributes.txt │ │ │ ├── CSS.AllowImportant.txt │ │ │ ├── CSS.AllowTricky.txt │ │ │ ├── CSS.AllowedFonts.txt │ │ │ ├── CSS.AllowedProperties.txt │ │ │ ├── CSS.DefinitionRev.txt │ │ │ ├── CSS.ForbiddenProperties.txt │ │ │ ├── CSS.MaxImgLength.txt │ │ │ ├── CSS.Proprietary.txt │ │ │ ├── CSS.Trusted.txt │ │ │ ├── Cache.DefinitionImpl.txt │ │ │ ├── Cache.SerializerPath.txt │ │ │ ├── Cache.SerializerPermissions.txt │ │ │ ├── Core.AggressivelyFixLt.txt │ │ │ ├── Core.AllowHostnameUnderscore.txt │ │ │ ├── Core.CollectErrors.txt │ │ │ ├── Core.ColorKeywords.txt │ │ │ ├── Core.ConvertDocumentToFragment.txt │ │ │ ├── Core.DirectLexLineNumberSyncInterval.txt │ │ │ ├── Core.DisableExcludes.txt │ │ │ ├── Core.EnableIDNA.txt │ │ │ ├── Core.Encoding.txt │ │ │ ├── Core.EscapeInvalidChildren.txt │ │ │ ├── Core.EscapeInvalidTags.txt │ │ │ ├── Core.EscapeNonASCIICharacters.txt │ │ │ ├── Core.HiddenElements.txt │ │ │ ├── Core.Language.txt │ │ │ ├── Core.LexerImpl.txt │ │ │ ├── Core.MaintainLineNumbers.txt │ │ │ ├── Core.NormalizeNewlines.txt │ │ │ ├── Core.RemoveInvalidImg.txt │ │ │ ├── Core.RemoveProcessingInstructions.txt │ │ │ ├── Core.RemoveScriptContents.txt │ │ │ ├── Filter.Custom.txt │ │ │ ├── Filter.ExtractStyleBlocks.Escaping.txt │ │ │ ├── Filter.ExtractStyleBlocks.Scope.txt │ │ │ ├── Filter.ExtractStyleBlocks.TidyImpl.txt │ │ │ ├── Filter.ExtractStyleBlocks.txt │ │ │ ├── Filter.YouTube.txt │ │ │ ├── HTML.Allowed.txt │ │ │ ├── HTML.AllowedAttributes.txt │ │ │ ├── HTML.AllowedComments.txt │ │ │ ├── HTML.AllowedCommentsRegexp.txt │ │ │ ├── HTML.AllowedElements.txt │ │ │ ├── HTML.AllowedModules.txt │ │ │ ├── HTML.Attr.Name.UseCDATA.txt │ │ │ ├── HTML.BlockWrapper.txt │ │ │ ├── HTML.CoreModules.txt │ │ │ ├── HTML.CustomDoctype.txt │ │ │ ├── HTML.DefinitionID.txt │ │ │ ├── HTML.DefinitionRev.txt │ │ │ ├── HTML.Doctype.txt │ │ │ ├── HTML.FlashAllowFullScreen.txt │ │ │ ├── HTML.ForbiddenAttributes.txt │ │ │ ├── HTML.ForbiddenElements.txt │ │ │ ├── HTML.MaxImgLength.txt │ │ │ ├── HTML.Nofollow.txt │ │ │ ├── HTML.Parent.txt │ │ │ ├── HTML.Proprietary.txt │ │ │ ├── HTML.SafeEmbed.txt │ │ │ ├── HTML.SafeIframe.txt │ │ │ ├── HTML.SafeObject.txt │ │ │ ├── HTML.SafeScripting.txt │ │ │ ├── HTML.Strict.txt │ │ │ ├── HTML.TargetBlank.txt │ │ │ ├── HTML.TidyAdd.txt │ │ │ ├── HTML.TidyLevel.txt │ │ │ ├── HTML.TidyRemove.txt │ │ │ ├── HTML.Trusted.txt │ │ │ ├── HTML.XHTML.txt │ │ │ ├── Output.CommentScriptContents.txt │ │ │ ├── Output.FixInnerHTML.txt │ │ │ ├── Output.FlashCompat.txt │ │ │ ├── Output.Newline.txt │ │ │ ├── Output.SortAttr.txt │ │ │ ├── Output.TidyFormat.txt │ │ │ ├── Test.ForceNoIconv.txt │ │ │ ├── URI.AllowedSchemes.txt │ │ │ ├── URI.Base.txt │ │ │ ├── URI.DefaultScheme.txt │ │ │ ├── URI.DefinitionID.txt │ │ │ ├── URI.DefinitionRev.txt │ │ │ ├── URI.Disable.txt │ │ │ ├── URI.DisableExternal.txt │ │ │ ├── URI.DisableExternalResources.txt │ │ │ ├── URI.DisableResources.txt │ │ │ ├── URI.Host.txt │ │ │ ├── URI.HostBlacklist.txt │ │ │ ├── URI.MakeAbsolute.txt │ │ │ ├── URI.Munge.txt │ │ │ ├── URI.MungeResources.txt │ │ │ ├── URI.MungeSecretKey.txt │ │ │ ├── URI.OverrideAllowedSchemes.txt │ │ │ ├── URI.SafeIframeRegexp.txt │ │ │ └── info.ini │ │ └── schema.ser │ ├── EntityLookup/ │ │ └── entities.ser │ ├── Filter/ │ │ ├── ExtractStyleBlocks.php │ │ └── YouTube.php │ ├── Language/ │ │ ├── classes/ │ │ │ └── en-x-test.php │ │ └── messages/ │ │ ├── en-x-test.php │ │ ├── en-x-testmini.php │ │ └── en.php │ ├── Lexer/ │ │ └── PH5P.php │ ├── Printer/ │ │ ├── CSSDefinition.php │ │ ├── ConfigForm.css │ │ ├── ConfigForm.js │ │ ├── ConfigForm.php │ │ └── HTMLDefinition.php │ └── Printer.php ├── module/ │ ├── blog/ │ │ ├── control.php │ │ ├── css/ │ │ │ └── common.css │ │ ├── lang/ │ │ │ ├── en.php │ │ │ └── zh-cn.php │ │ ├── model.php │ │ └── view/ │ │ ├── create.html.php │ │ ├── edit.html.php │ │ ├── index.html.php │ │ └── view.html.php │ ├── common/ │ │ ├── lang/ │ │ │ ├── en.php │ │ │ └── zh-cn.php │ │ ├── model.php │ │ └── view/ │ │ ├── footer.html.php │ │ ├── header.html.php │ │ └── nav.html.php │ ├── file/ │ │ ├── config.php │ │ ├── control.php │ │ ├── lang/ │ │ │ ├── en.php │ │ │ ├── zh-cn.php │ │ │ └── zh-tw.php │ │ └── model.php │ └── index/ │ ├── control.php │ ├── css/ │ │ └── index.css │ ├── js/ │ │ └── index.js │ ├── lang/ │ │ ├── en.php │ │ └── zh-cn.php │ └── view/ │ └── index.html.php ├── theme/ │ └── my.css └── tools/ ├── build/ │ ├── debian/ │ │ ├── README.Debian │ │ ├── changelog │ │ ├── compat │ │ ├── control │ │ ├── copyright │ │ ├── dirs │ │ ├── docs │ │ ├── files │ │ ├── postinst.ex │ │ ├── postrm.ex │ │ ├── preinst.ex │ │ ├── prerm.ex │ │ ├── rules │ │ └── watch.ex │ └── pear/ │ └── package.xml ├── chanzhi/ │ ├── control.class.php │ ├── helper.class.php │ ├── myrouter.class.php │ └── process.php ├── ranzhi/ │ ├── helper.class.php │ ├── myrouter.class.php │ └── process.php └── zentao/ ├── front.class.php ├── myrouter.class.php └── process.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ config/my.php www/data/ release/* tmp/extensions/* tmp/log/* tmp/model/* tmp/cache/* tmp/extension/* .gitkeep ================================================ FILE: .htaccess ================================================ # Add support for PATH_INFO method. RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule (.*)$ index.php/$1 [L] # Set expires for static files. ExpiresActive On ExpiresByType image/gif A2592000 ExpiresByType image/jpeg A2592000 ExpiresByType image/png A2592000 ExpiresByType image/x-icon A2592000 ExpiresByType application/javascript A2592000 ExpiresByType application/x-shockwave-flash A2592000 ExpiresByType text/css A604800 # Deflate static files. AddOutputFilterByType DEFLATE text/html text/css application/javascript # Turn off FileEtag. FileEtag none ================================================ FILE: Makefile ================================================ VERSION=$(shell head -n 1 VERSION) all: zip clean: rm -fr *-stamp rm -fr debian/zentaophp rm -fr zentaoPHP-*.tgz rm -fr package.xml rm -fr ztphp* rm -fr *.zip rm -fr zentaophp rm -fr ztphpapi pear: cp app/cli/ztphp.sh ./ztphp cp app/cli/ztphp.bat ./ztphp.bat cp package/pear/package.xml . pear package pear-install: sudo pear uninstall zentaophp sudo pear install zentaoPHP*.tgz deb: dpkg-buildpackage -rfakeroot zip: mkdir zentaophp cp -fr {config,db,favicon.ico,framework,index.php,js,lib,module,theme,.htaccess} zentaophp rm -fr zentaophp/config/my.php rm -fr zentaophp/framework/tests rm -fr zentaophp/config/sites/* rm -fr zentaophp/config/domain.php mkdir zentaophp/tmp/log -p chmod 777 -R zentaophp/tmp/log find zentaophp -name .git |xargs rm -fr zip -r -9 zentaoPHP.$(VERSION).zip zentaophp rm -fr zentaophp ztphpdoc: phpdoc -d framework,lib -t ztphpapi -o HTML:frames:phphtmllib -ti "zentaophp framework help" -s on -pp on -i *test* phpdoc -d framework,lib -t ztphpapi.chm -o chm:default:default -ti "zentaophp framework help" -s on -pp on -i *test* ================================================ FILE: VERSION ================================================ 3.0 ================================================ FILE: config/config.php ================================================ version = '3.1'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. $config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP. $config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time. $config->timezone = 'Asia/Shanghai'; // 时区设置。 The time zone setting, for more see http://www.php.net/manual/en/timezones.php. $config->webRoot = ''; // URL根目录。 The root path of the url. /* 框架路由相关设置。Routing settings. */ $config->requestType = 'PATH_INFO'; // 请求类型:PATH_INFO|PATHINFO2|GET。 The request type: PATH_INFO|PATH_INFO2|GET. $config->requestFix = '-'; // PATH_INFO和PATH_INFO2模式的分隔符。 The divider in the url when PATH_INFO|PATH_INFO2. $config->moduleVar = 'm'; // 请求类型为GET:模块变量名。 requestType=GET: the module var name. $config->methodVar = 'f'; // 请求类型为GET:模块变量名。 requestType=GET: the method var name. $config->viewVar = 't'; // 请求类型为GET:视图变量名。 requestType=GET: the view var name. $config->sessionVar = 'sid'; // 请求类型为GET:session变量名。 requestType=GET: the session var name. $config->views = ',html,json,mhtml,'; // 支持的视图类型。 Supported view formats. /* 支持的主题和语言。Supported thems and languages. */ $config->themes['default'] = 'default'; $config->langs['zh-cn'] = '简体'; $config->langs['zh-tw'] = '繁体'; $config->langs['en'] = 'English'; /* 设备类型视图文件前缀。The prefix for view file for different device. */ $config->devicePrefix['mhtml'] = 'm.'; /* 默认值设置。Default settings. */ $config->default = new stdclass(); $config->default->view = 'html'; //默认视图。 Default view. $config->default->lang = 'en'; //默认语言。 Default language. $config->default->theme = 'default'; //默认主题。 Default theme. $config->default->module = 'index'; //默认模块。 Default module. $config->default->method = 'index'; //默认方法。 Default method. /* 数据库设置。Database settings. */ $config->db = new stdclass(); $config->slaveDB = new stdclass(); $config->db->persistant = false; // 是否为持续连接。 Pconnect or not. $config->db->driver = 'mysql'; // 目前只支持MySQL数据库。Must be MySQL. Don't support other database server yet. $config->db->encoding = 'UTF8'; // 数据库编码。 Encoding of database. $config->db->strictMode = false; // 关闭MySQL的严格模式。 Turn off the strict mode of MySQL. $config->db->prefix = ''; // 数据库表名前缀。 The prefix of the table name. $config->slaveDB->persistant = false; $config->slaveDB->driver = 'mysql'; $config->slaveDB->encoding = 'UTF8'; $config->slaveDB->strictMode = false; /* 可用域名后缀列表。Domain postfix lists. */ $config->domainPostfix = "|com|com.cn|com.hk|com.tw|com.vc|edu.cn|es|"; $config->domainPostfix .= "|eu|fm|gov.cn|gs|hk|im|in|info|jp|kr|la|me|"; $config->domainPostfix .= "|mobi|my|name|net|net.cn|org|org.cn|pk|pro|"; $config->domainPostfix .= "|sg|so|tel|tk|to|travel|tv|tw|uk|us|ws|"; $config->domainPostfix .= "|ac.cn|bj.cn|sh.cn|tj.cn|cq.cn|he.cn|sn.cn|"; $config->domainPostfix .= "|sx.cn|nm.cn|ln.cn|jl.cn|hl.cn|js.cn|zj.cn|"; $config->domainPostfix .= "|ah.cn|fj.cn|jx.cn|sd.cn|ha.cn|hb.cn|hn.cn|"; $config->domainPostfix .= "|gd.cn|gx.cn|hi.cn|sc.cn|gz.cn|yn.cn|gs.cn|pub|pw|"; $config->domainPostfix .= "|qh.cn|nx.cn|xj.cn|tw.cn|hk.cn|mo.cn|xz.cn|xyz|wang|"; $config->domainPostfix .= "|ae|asia|biz|cc|cd|cm|cn|co|co.jp|co.kr|co.uk|"; $config->domainPostfix .= "|top|ren|club|space|tm|website|cool|company|city|email|"; $config->domainPostfix .= "|market|software|ninja|bike|today|life|co.il|io|"; $config->domainPostfix .= "|mn|ph|ps|tl|uz|vn|co.nz|cz|gg|gl|gr|je|md|me.uk|org.uk|pl|si|sx|vg|ag|"; $config->domainPostfix .= "|bz|cl|ec|gd|gy|ht|lc|ms|mx|pe|tc|vc|ac|bi|mg|mu|sc|as|com.sb|cx|ki|nf|sh|"; $config->domainPostfix .= "|rocks|social|co.com|bio|reviews|link|sexy|us.com|consulting|moda|desi|"; $config->domainPostfix .= "|menu|info|events|webcam|dating|vacations|flights|cruises|global|ca|guru|"; $config->domainPostfix .= "|futbol|rentals|dance|lawyer|attorney|democrat|republican|actor|condos|immobilien|"; $config->domainPostfix .= "|villas|foundation|expert|works|tools|watch|zone|bargains|agency|best|solar|"; $config->domainPostfix .= "|farm|pics|photo|marketing|holiday|gift|buzz|guitars|trade|construction|"; $config->domainPostfix .= "|international|house|coffee|florist|rich|ceo|camp|education|repair|win|site|"; /* 系统框架配置。Framework settings. */ $config->framework = new stdclass(); $config->framework->autoConnectDB = true; // 是否自动连接数据库。 Whether auto connect database or not. $config->framework->multiLanguage = false; // 是否启用多语言功能。 Whether enable multi lanuage or not. $config->framework->multiTheme = false; // 是否启用多风格功能。 Whether enable multi theme or not. $config->framework->multiSite = false; // 是否启用多站点模式。 Whether enable multi site mode or not. $config->framework->extensionLevel = 0; // 0=>无扩展,1=>公共扩展,2=>站点扩展 0=>no extension, 1=> common extension, 2=> every site has it's extension. $config->framework->jsWithPrefix = true; // js::set()输出的时候是否增加前缀。 When us js::set(), add prefix or not. $config->framework->filterBadKeys = true; // 是否过滤不合要求的键值。 Whether filter bad keys or not. $config->framework->filterTrojan = true; // 是否过滤木马攻击代码。 Whether strip trojan code or not. $config->framework->filterXSS = true; // 是否过滤XSS攻击代码。 Whether strip xss code or not. $config->framework->filterParam = 2; // 1=>默认过滤,2=>开启过滤参数功能。0=>default filter 2=>Whether strip param. $config->framework->purifier = true; // 是否对数据做purifier处理。 Whether purifier data or not. $config->framework->logDays = 14; // 日志文件保存的天数。 The days to save log files. $config->framework->detectDevice['zh-cn'] = false; // 在zh-cn语言情况下,是否启用设备检测功能。 Whether enable device detect or not. $config->framework->detectDevice['zh-tw'] = false; // 在zh-tw语言情况下,是否启用设备检测功能。 Whether enable device detect or not. $config->framework->detectDevice['en'] = false; // 在en语言情况下,是否启用设备检测功能。 Whether enable device detect or not. /* 文件上传设置。 Upload settings. */ $config->file = new stdclass(); $config->file->dangers = 'php,php3,php4,phtml,php5,jsp,py,rb,asp,aspx,ashx,asa,cer,cdx,aspl,shtm,shtml,html,htm'; $config->file->allowed = 'txt,doc,docx,dot,wps,wri,pdf,ppt,xls,xlsx,ett,xlt,xlsm,csv,jpg,jpeg,png,psd,gif,ico,bmp,swf,avi,rmvb,rm,mp3,mp4,3gp,flv,mov,movie,rar,zip,bz,bz2,tar,gz'; /* 配置参数过滤。Filter param settings. */ $filterConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'filter.php'; if(file_exists($filterConfig)) include $filterConfig; /* 引用自定义的配置。 Include the custom config file. */ $myConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'my.php'; if(file_exists($myConfig)) include $myConfig; ================================================ FILE: config/filter.php ================================================ rules = new stdclass(); $filter->rules->md5 = '/^[a-z0-9]{32}$/'; $filter->rules->base64 = '/^[a-zA-Z0-9\+\/\=]+$/'; $filter->rules->checked = '/^[0-9,]+$/'; $filter->rules->idList = '/^[0-9\|]+$/'; $filter->rules->lang = '/^[a-zA-Z_\-]+$/'; $filter->rules->any = '/./'; $filter->rules->number = '/^[0-9]+$/'; $filter->rules->orderBy = '/^\w+_(desc|asc)$/i'; $filter->rules->word = '/^\w+$/'; $filter->rules->paramName = '/^[a-zA-Z0-9_\.]+$/'; $filter->rules->paramValue = '/^[a-zA-Z0-9=_\-]+$/'; $filter->default = new stdclass(); $filter->default->moduleName = 'code'; $filter->default->methodName = 'code'; $filter->default->paramName = 'reg::paramName'; $filter->default->paramValue = 'reg::paramValue'; $filter->default->get['onlybody'] = 'equal::yes'; $filter->default->get['HTTP_X_REQUESTED_WITH'] = 'equal::XMLHttpRequest'; ================================================ FILE: config/my.example.php ================================================ installed = true; $config->debug = true; $config->requestType = 'GET'; $config->requestFix = '-'; $config->webRoot = '/'; $config->db->host = 'localhost'; $config->db->port = '3306'; $config->db->name = 'demo'; $config->db->user = 'root'; $config->db->password = ''; /* 如果需要配置主从数据库,取消注释即可。To use master and slave database feature, uncomment this. */ //$config->slaveDB->host = 'localhost'; //$config->slaveDB->port = '3306'; //$config->slaveDB->name = 'demo'; //$config->slaveDB->user = 'root'; //$config->slaveDB->password = ''; /* 框架功能开关参数。Use these params to enable or disable some features of framework. */ $config->framework->autoConnectDB = true; // 是否自动连接数据库。 Whether auto connect database or not. $config->framework->multiLanguage = true; // 是否启用多语言功能。 Whether enable multi lanuage or not. $config->framework->multiTheme = false; // 是否启用多风格功能。 Whether enable multi theme or not. $config->framework->detectDevice = false; // 是否启用设备检测功能。 Whether enable device detect or not. $config->framework->multiSite = false; // 是否启用多站点模式。 Whether enable multi site mode or not. $config->framework->extensionLevel= 0; // 0=>无扩展,1=>公共扩展,2=>站点扩展 0=>no extension, 1=> common extension, 2=> every site has it's extension. $config->framework->jsWithPrefix = true; // js::set()输出的时候是否增加前缀。 When us js::set(), add prefix or not. $config->framework->filterBadKeys = true; // 是否过滤不合要求的键值。 Whether filter bad keys or not. $config->framework->filterTrojan = true; // 是否过滤木马攻击代码。 Whether strip trojan code or not. $config->framework->filterXSS = true; // 是否过滤XSS攻击代码。 Whether strip xss code or not. $config->framework->purifier = true; // 是否对数据做purifier处理。 Whether purifier data or not. $config->framework->logDays = 14; // 日志文件保存的天数。 The days to save log files. ================================================ FILE: db/blog.sql ================================================ CREATE TABLE IF NOT EXISTS `blog` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `title` char(120) NOT NULL, `content` text NOT NULL, `date` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; ================================================ FILE: framework/base/control.class.php ================================================ post->key来引用$_POST变量。 * The $post object, useer can access a post var by $this->post->key. * * @var object * @access public */ public $post; /** * $get对象,用户可以通过$this->get->key来引用$_GET变量。 * The $get object, useer can access a get var by $this->get->key. * * @var object * @access public */ public $get; /** * $session对象,用户可以通过$this->session->key来引用$_SESSION变量。 * The $session object, useer can access a session var by $this->session->key. * * @var object * @access public */ public $session; /** * $server对象,用户可以通过$this->server->key来引用$_SERVER变量。 * The $server object, useer can access a server var by $this->server->key. * * @var object * @access public */ public $server; /** * $cookie对象,用户可以通过$this->cookie->key来引用$_COOKIE变量。 * The $cookie object, useer can access a cookie var by $this->cookie->key. * * @var object * @access public */ public $cookie; /** * 当前模块的名称。 * The name of current module. * * @var string * @access public */ public $moduleName; /** * $view用于存放从control传到view视图的数据。 * The vars assigned to the view page. * * @var object * @access public */ public $view; /** * 视图的类型,比如html, json。 * The type of the view, such html, json. * * @var string * @access public */ public $viewType; /** * 输出到浏览器的内容。 * The content to display. * * @var string * @access public */ public $output; /** * 客户端设备。 * The client device. * * @var string * @access public */ public $clientDevice; /** * 不同设备下视图文件的前缀。 * The prefix of view file for mobile or PC. * * @var string * @access public */ public $devicePrefix; /** * 构造方法。 * * 1. 将全局变量设为baseControl类的成员变量,方便baseControl的派生类调用; * 2. 设置当前模块,读取该模块的model类; * 3. 初始化$view视图类。 * * The construct function. * * 1. global the global vars, refer them by the class member such as $this->app. * 2. set the pathes of current module, and load it's model class. * 3. auto assign the $lang and $config to the view. * * @param string $moduleName * @param string $methodName * @param string $appName * @access public * @return void */ public function __construct($moduleName = '', $methodName = '', $appName = '') { /* * 将全局变量设为baseControl类的成员变量,方便baseControl的派生类调用。 * Global the globals, and refer them as a class member. */ global $app, $config, $lang, $dbh, $common; $this->app = $app; $this->config = $config; $this->lang = $lang; $this->dbh = $dbh; $this->viewType = $this->app->getViewType(); $this->appName = $appName ? $appName : $this->app->getAppName(); /** * 设置当前模块,读取该模块的model类。 * Load the model file auto. */ $this->setModuleName($moduleName); $this->setMethodName($methodName); $this->loadModel($this->moduleName, $appName); /** * 如果客户端是手机的话,视图文件增加m.前缀。 * If the clent is mobile, add m. as prefix for view file. */ $this->setClientDevice(); $this->setDevicePrefix(); /** * 初始化$view视图类。 * Init the view vars. */ $this->view = new stdclass(); $this->view->app = $app; $this->view->lang = $lang; $this->view->config = $config; $this->view->common = $common; $this->view->title = ''; /** * 设置超级变量,从$app引用过来。 * Set super vars. */ $this->setSuperVars(); } //-------------------- Model相关方法(Model related methods) --------------------// /* * 设置模块名。 * Set the module name. * * @param string $moduleName 模块名,如果为空,则从$app中获取. The module name, if empty, get it from $app. * @access public * @return void */ public function setModuleName($moduleName = '') { $this->moduleName = $moduleName ? strtolower($moduleName) : $this->app->getModuleName(); } /** * 设置方法名。 * Set the method name. * * @param string $methodName 方法名,如果为空,则从$app中获取。The method name, if empty, get it from $app. * @access public * @return void */ public function setMethodName($methodName = '') { $this->methodName = $methodName ? strtolower($methodName) : $this->app->getMethodName(); } /** * 加载指定模块的model文件。 * Load the model file of one module. * * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. * @param string $appName The app name, if empty, use current app's name. * @access public * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. */ public function loadModel($moduleName = '', $appName = '') { if(empty($moduleName)) $moduleName = $this->moduleName; if(empty($appName)) $appName = $this->appName; global $loadedModels; if(isset($loadedModels[$appName][$moduleName])) { $this->$moduleName = $loadedModels[$appName][$moduleName]; $this->dao = $this->$moduleName->dao; return $this->$moduleName; } $modelFile = $this->app->setModelFile($moduleName, $appName); /** * 如果没有model文件,尝试加载config配置信息。 * If no model file, try load config. */ if(!helper::import($modelFile)) { $this->app->loadModuleConfig($moduleName, $appName); $this->app->loadLang($moduleName, $appName); $this->dao = new dao(); return false; } /** * 如果没有扩展文件,model类名是$moduleName + 'model',如果有扩展,还需要增加ext前缀。 * If no extension file, model class name is $moduleName + 'model', else with 'ext' as the prefix. */ $modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; if(!class_exists($modelClass)) { $modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); } /** * 初始化model对象,在control对象中可以通过$this->$moduleName来引用。同时将dao对象赋为control对象的成员变量,方便引用。 * Init the model object thus you can try $this->$moduleName to access it. Also assign the $dao object as a member of control object. */ $loadedModels[$appName][$moduleName] = new $modelClass($appName); $this->$moduleName = $loadedModels[$appName][$moduleName]; $this->dao = $this->$moduleName->dao; return $this->$moduleName; } /** * 设置超级全局变量,方便直接引用。 * Set the super vars. * * @access public * @return void */ public function setSuperVars() { $this->post = $this->app->post; $this->get = $this->app->get; $this->server = $this->app->server; $this->session = $this->app->session; $this->cookie = $this->app->cookie; } /** * 设置客户端的设备类型。 * Set client device. * * @access public * @return void */ public function setClientDevice() { $this->clientDevice = $this->app->clientDevice; } /** * 如果客户端是手机的话,视图文件增加m.前缀。 * If the clent is mobile, add m. as prefix for view file. * * @access public * @return void */ public function setDevicePrefix() { $this->devicePrefix = zget($this->config->devicePrefix, $this->viewType, ''); } //-------------------- 视图相关方法(View related methods) --------------------// /** * 设置视图文件:主视图文件,扩展视图文件, 站点扩展视图文件,以及钩子脚本。 * Set view files: the main file, extension view file, site extension view file and hook files. * * @param string $moduleName module name * @param string $methodName method name * @access public * @return string the view file */ public function setViewFile($moduleName, $methodName) { $moduleName = strtolower(trim($moduleName)); $methodName = strtolower(trim($methodName)); $modulePath = $this->app->getModulePath($this->appName, $moduleName); $viewExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'view'); $viewType = $this->viewType == 'mhtml' ? 'html' : $this->viewType; $mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php'; $viewFile = $mainViewFile; if(!empty($viewExtPath)) { $commonExtViewFile = $viewExtPath['common'] . $this->devicePrefix . $methodName . ".{$viewType}.php"; $siteExtViewFile = empty($viewExtPath['site']) ? '' : $viewExtPath['site'] . $this->devicePrefix . $methodName . ".{$viewType}.php"; $viewFile = file_exists($commonExtViewFile) ? $commonExtViewFile : $mainViewFile; $viewFile = (!empty($siteExtViewFile) and file_exists($siteExtViewFile)) ? $siteExtViewFile : $viewFile; if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, $exit = true); $commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php"); $siteExtHookFiles = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php"); $extHookFiles = array_merge((array) $commonExtHookFiles, (array) $siteExtHookFiles); } if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles); return $viewFile; } /** * 获取某一个视图文件的扩展。 * Get the extension file of an view. * * @param string $viewFile * @access public * @return string|bool If extension view file exists, return the path. Else return fasle. */ public function getExtViewFile($viewFile) { /** * 首先找sitecode下的扩展文件,如果没有,再找ext下的扩展文件。 * Find extViewFile in ext/_$siteCode/view first, then try ext/view/. */ if($this->app->siteCode) { $extPath = dirname(dirname(realpath($viewFile))) . "/ext/_{$this->app->siteCode}/view"; $extViewFile = $extPath . basename($viewFile); if(file_exists($extViewFile)) { helper::cd($extPath); return $extViewFile; } } $extPath = dirname(dirname(realpath($viewFile))) . '/ext/view/'; $extViewFile = $extPath . basename($viewFile); if(file_exists($extViewFile)) { helper::cd($extPath); return $extViewFile; } return false; } /** * 获取适用于当前方法的css:该模块公用的css + 当前方法的css + 扩展的css。 * Get css codes applied to current method: module common css + method css + extension css. * * @param string $moduleName * @param string $methodName * @access public * @return string */ public function getCSS($moduleName, $methodName) { $moduleName = strtolower(trim($moduleName)); $methodName = strtolower(trim($methodName)); $modulePath = $this->app->getModulePath($this->appName, $moduleName); $cssExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'css') ; $css = ''; $mainCssFile = $modulePath . 'css' . DS . $this->devicePrefix . 'common.css'; $methodCssFile = $modulePath . 'css' . DS . $this->devicePrefix . $methodName . '.css'; if(file_exists($mainCssFile)) $css .= file_get_contents($mainCssFile); if(is_file($methodCssFile)) $css .= file_get_contents($methodCssFile); if(!empty($cssExtPath)) { $cssMethodExt = $cssExtPath['common'] . $methodName . DS; $cssCommonExt = $cssExtPath['common'] . 'common' . DS; $cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css'); if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); $cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css'); if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); if(!empty($cssExtPath['site'])) { $cssMethodExt = $cssExtPath['site'] . $methodName . DS; $cssCommonExt = $cssExtPath['site'] . 'common' . DS; $cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css'); if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); $cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css'); if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile); } } return $css; } /** * 获取适用于当前方法的js:该模块公用的js + 当前方法的js + 扩展的js。 * Get js codes applied to current method: module common js + method js + extension js. * * @param string $moduleName * @param string $methodName * @access public * @return string */ public function getJS($moduleName, $methodName) { $moduleName = strtolower(trim($moduleName)); $methodName = strtolower(trim($methodName)); $modulePath = $this->app->getModulePath($this->appName, $moduleName); $jsExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'js'); $js = ''; $mainJsFile = $modulePath . 'js' . DS . $this->devicePrefix . 'common.js'; $methodJsFile = $modulePath . 'js' . DS . $this->devicePrefix . $methodName . '.js'; if(file_exists($mainJsFile)) $js .= file_get_contents($mainJsFile); if(is_file($methodJsFile)) $js .= file_get_contents($methodJsFile); if(!empty($jsExtPath)) { $jsMethodExt = $jsExtPath['common'] . $methodName . DS; $jsCommonExt = $jsExtPath['common'] . 'common' . DS; $jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js'); if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); $jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js'); if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); if(!empty($jsExtPath['site'])) { $jsMethodExt = $jsExtPath['site'] . $methodName . DS; $jsCommonExt = $jsExtPath['site'] . 'common' . DS; $jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js'); if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); $jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js'); if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile); } } return $js; } /** * 向$view传递一个变量。 * Assign one var to the view vars. * * @param string $name the name. * @param mixed $value the value. * @access public * @return void */ public function assign($name, $value) { $this->view->$name = $value; } /** * 清空$output。 * Clear the output. * * @access public * @return void */ public function clear() { $this->output = ''; } /** * 渲染视图文件。 * Parse view file. * * @param string $moduleName module name, if empty, use current module. * @param string $methodName method name, if empty, use current method. * @access public * @return string the parsed result. */ public function parse($moduleName = '', $methodName = '') { if(empty($moduleName)) $moduleName = $this->moduleName; if(empty($methodName)) $methodName = $this->methodName; if($this->viewType == 'json') $this->parseJSON($moduleName, $methodName); if($this->viewType != 'json') $this->parseDefault($moduleName, $methodName); return $this->output; } /** * 渲染json格式。 * Parse json format. * * @param string $moduleName module name * @param string $methodName method name * @access public * @return void */ public function parseJSON($moduleName, $methodName) { unset($this->view->app); unset($this->view->config); unset($this->view->lang); unset($this->view->header); unset($this->view->position); unset($this->view->moduleTree); unset($this->view->common); unset($this->view->pager->app); unset($this->view->pager->lang); $output['status'] = is_object($this->view) ? 'success' : 'fail'; $output['data'] = json_encode($this->view); $output['md5'] = md5(json_encode($this->view)); $this->output = json_encode($output); } /** * 默认渲染方法,适用于viewType = html的时候。 * Default parse method when viewType != json, like html. * * @param string $moduleName module name * @param string $methodName method name * @access public * @return void */ public function parseDefault($moduleName, $methodName) { /** * 设置视图文件。(PHP7有一个bug,不能直接$viewFile = $this->setViewFile())。 * Set viewFile. (Can't assign $viewFile = $this->setViewFile() directly because one php7's bug.) */ $results = $this->setViewFile($moduleName, $methodName); $viewFile = $results; if(is_array($results)) extract($results); /** * 获得当前页面的CSS和JS。 * Get css and js codes for current method. */ $css = $this->getCSS($moduleName, $methodName); $js = $this->getJS($moduleName, $methodName); if($css) $this->view->pageCSS = $css; if($js) $this->view->pageJS = $js; /** * 切换到视图文件所在的目录,以保证视图文件里面的include语句能够正常运行。 * Change the dir to the view file to keep the relative pathes work. */ $currentPWD = getcwd(); chdir(dirname($viewFile)); /** * 使用extract安定ob方法渲染$viewFile里面的代码。 * Use extract and ob functions to eval the codes in $viewFile. */ extract((array)$this->view); ob_start(); include $viewFile; if(isset($hookFiles)) foreach($hookFiles as $hookFile) if(file_exists($hookFile)) include $hookFile; $this->output .= ob_get_contents(); ob_end_clean(); /** * 渲染完毕后,再切换回之前的路径。 * At the end, chang the dir to the previous. */ chdir($currentPWD); } /** * 获取一个方法的输出内容,这样我们可以在一个方法里获取其他模块方法的内容。 * 如果模块名为空,则调用该模块、该方法;如果设置了模块名,调用指定模块指定方法。 * * Get the output of one module's one method as a string, thus in one module's method, can fetch other module's content. * If the module name is empty, then use the current module and method. If set, use the user defined module and method. * * @param string $moduleName module name. * @param string $methodName method name. * @param array $params params. * @access public * @return string the parsed html. */ public function fetch($moduleName = '', $methodName = '', $params = array(), $appName = '') { /** * 如果模块名为空,则调用该模块、该方法。 * If the module name is empty, then use the current module and method. */ if($moduleName == '') $moduleName = $this->moduleName; if($methodName == '') $methodName = $this->methodName; if($appName == '') $appName = $this->appName; if($moduleName == $this->moduleName and $methodName == $this->methodName) { $this->parse($moduleName, $methodName); return $this->output; } $currentModuleName = $this->moduleName; $currentMethodName = $this->methodName; $currentAppName = $this->appName; $currentParams = $this->app->getParams(); /** * 设置调用指定模块的指定方法。 * chang the dir to the previous. */ $this->app->setModuleName($moduleName); $this->app->setMethodName($methodName); if(!is_array($params)) parse_str($params, $params); $this->app->params = $params; $currentPWD = getcwd(); /** * 设置引用的文件和路径。 * Set the pathes and files to included. */ $modulePath = $this->app->getModulePath($appName, $moduleName); $moduleControlFile = $modulePath . 'control.php'; $actionExtPath = $this->app->getModuleExtPath($appName, $moduleName, 'control'); $file2Included = $moduleControlFile; if(!empty($actionExtPath)) { /** * 设置公共扩展。 * set common extension. */ $commonActionExtFile = $actionExtPath['common'] . strtolower($methodName) . '.php'; $file2Included = file_exists($commonActionExtFile) ? $commonActionExtFile : $moduleControlFile; if(!empty($actionExtPath['site'])) { /** * 设置站点扩展。 * every site has it's extension. */ $siteActionExtFile = $actionExtPath['site'] . strtolower($methodName) . '.php'; $file2Included = file_exists($siteActionExtFile) ? $siteActionExtFile : $file2Included; } } /** * 加载控制器文件。 * Load the control file. */ if(!is_file($file2Included)) $this->app->triggerError("The control file $file2Included not found", __FILE__, __LINE__, $exit = true); chdir(dirname($file2Included)); if($moduleName != $this->moduleName) helper::import($file2Included); /** * 设置调用的类名。 * Set the name of the class to be called. */ $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName; if(!class_exists($className)) $this->app->triggerError(" The class $className not found", __FILE__, __LINE__, $exit = true); /** * 解析参数,创建模块control对象。 * Parse the params, create the $module control object. */ $module = new $className($moduleName, $methodName, $appName); /** * 调用对应方法,使用ob方法获取输出内容。 * Call the method and use ob function to get the output. */ ob_start(); call_user_func_array(array($module, $methodName), $params); $output = ob_get_contents(); ob_end_clean(); unset($module); /** * 切换回之前的模块和方法。 * Chang the module、method to the previous. */ $this->app->setModuleName($currentModuleName); $this->app->setMethodName($currentMethodName); $this->app->params = $currentParams; chdir($currentPWD); /** * 返回内容。 * Return the content. */ return $output; } /** * 向浏览器输出内容。 * Print the content of the view. * * @param string $moduleName module name * @param string $methodName method name * @access public * @return void */ public function display($moduleName = '', $methodName = '') { if(empty($this->output)) $this->parse($moduleName, $methodName); echo $this->output; } /** * 直接输出data数据,通常用于ajax请求中。 * Send data directly, for ajax requests. * * @param misc $data * @param string $type * @access public * @return void */ public function send($data, $type = 'json') { if($type != 'json') die(); $data = (array) $data; if(helper::isAjaxRequest() or $this->viewType == 'json') print(json_encode($data)) and die(helper::removeUTF8Bom(ob_get_clean())); /** * 响应非ajax的请求。 * Response request not ajax. */ if(isset($data['result']) and $data['result'] == 'success') { if(!empty($data['message'])) echo js::alert($data['message']); $locate = isset($data['locate']) ? $data['locate'] : (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''); if(!empty($locate)) die(js::locate($locate)); die(isset($data['message']) ? $data['message'] : 'success'); } if(isset($data['result']) and $data['result'] == 'fail') { if(!empty($data['message'])) { $message = json_decode(json_encode((array)$data['message'])); foreach((array)$message as $item => $errors) $message->$item = implode(',', $errors); echo js::alert(strip_tags(implode(" ", (array) $message))); die(js::locate('back')); } die('fail'); } } /** * 创建一个模块方法的链接。 * Create a link to one method of one module. * * @param string $moduleName module name * @param string $methodName method name * @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2 * @param string $viewType the view type * @access public * @return string the link string. */ public function createLink($moduleName, $methodName = 'index', $vars = array(), $viewType = '', $onlybody = false) { if(empty($moduleName)) $moduleName = $this->moduleName; return helper::createLink($moduleName, $methodName, $vars, $viewType, $onlybody); } /** * 创建当前模块的一个方法链接。 * Create a link to the inner method of current module. * * @param string $methodName method name * @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2 * @param string $viewType the view type * @access public * @return string the link string. */ public function inlink($methodName = 'index', $vars = array(), $viewType = '', $onlybody = false) { return helper::createLink($this->moduleName, $methodName, $vars, $viewType, $onlybody); } /** * 重定向到另一个页面。 * Location to another page. * * @param string $url the target url. * @access public * @return void */ public function locate($url) { header("location: $url"); exit; } } ================================================ FILE: framework/base/helper.class.php ================================================ * db->user = 'wwccss'; * helper::setMember('lang', 'db.user', 'chunsheng.wang'); * ?> * * @param string $objName the var name of the object. * @param string $key the key of the member, can be parent.child. * @param mixed $value the value to be set. * @static * @access public * @return bool */ static public function setMember($objName, $key, $value) { global $$objName; if(!is_object($$objName) or empty($key)) return false; $key = str_replace('.', '->', $key); $value = serialize($value); $code = ("\$${objName}->{$key}=unserialize(<< * 'value1', 'var2' => 'value2'); * ?> * * @param string $moduleName module name, can pass appName like app.module. * @param string $methodName method name * @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2) or key1=value1&key2=value2 * @param string $viewType the view type * @param bool $onlyBody pass onlyBody=yes to the link thus the app can control the header and footer hide or show.. * @static * @access public * @return string the link string. */ static public function createLink($moduleName, $methodName = 'index', $vars = '', $viewType = '', $onlyBody = false) { /* 设置$appName和$moduleName。Set appName and moduleName. */ global $app, $config; if(strpos($moduleName, '.') !== false) { list($appName, $moduleName) = explode('.', $moduleName); } else { $appName = $app->getAppName(); } if(!empty($appName)) $appName .= '/'; /* 处理$viewType和$vars。Set $viewType and $vars. */ if(empty($viewType)) $viewType = $app->getViewType(); if(!is_array($vars)) parse_str($vars, $vars); /* 生成url链接的开始部分。Set the begin parts of the link. */ if($config->requestType == 'PATH_INFO') $link = $config->webRoot . $appName; if($config->requestType != 'PATH_INFO') $link = $config->webRoot . $appName . basename($_SERVER['SCRIPT_NAME']); if($config->requestType == 'PATH_INFO2') $link .= '/'; /** * #1: RequestType为GET。When the requestType is GET. * Input: moduleName=article&methodName=index&var1=value1. Output: ?m=article&f=index&var1=value1. * */ if($config->requestType == 'GET') { $link .= "?{$config->moduleVar}=$moduleName&{$config->methodVar}=$methodName"; if($viewType != 'html') $link .= "&{$config->viewVar}=" . $viewType; foreach($vars as $key => $value) $link .= "&$key=$value"; return self::processOnlyBodyParam($link, $onlyBody); } /** * #2: 方法名不是默认值或者是默认值,但有传参。MethodName equals the default method or vars not empty. * Input: moduleName=article&methodName=view. Output: article-view.html * Input: moduleName=article&methodName=view. Output: article-index-abc.html * */ if($methodName != $config->default->method or !empty($vars)) { $link .= "$moduleName{$config->requestFix}$methodName"; foreach($vars as $value) $link .= "{$config->requestFix}$value"; $link .= '.' . $viewType; return self::processOnlyBodyParam($link, $onlyBody); } /** * #3: 方法名为默认值且没有传参且模块名为默认值。MethodName is the default and moduleName is default and vars empty. * Input: moduleName=index&methodName=index. Output: index.html * */ if($moduleName == $config->default->module) { $link .= $config->default->method . '.' . $viewType; return self::processOnlyBodyParam($link, $onlyBody); } /** * #4: 方法名为默认值且没有传参且模块名不为默认值,viewType和app指定的相等。MethodName is default but moduleName not and viewType equal app's viewType.. * Input: moduleName=article&methodName=index&viewType=html. Output: /article/ * */ if($viewType == $app->getViewType()) { $link .= $moduleName . '/'; return self::processOnlyBodyParam($link, $onlyBody); } /** * #5: 方法名为默认值且没有传参且模块名不为默认值,viewType有另外指定。MethodName is default but moduleName not and viewType no equls app's viewType. * Input: moduleName=article&methodName=index&viewType=json. Output: /article.json * */ $link .= $moduleName . '.' . $viewType; return self::processOnlyBodyParam($link, $onlyBody); } /** * 处理onlyBody 参数。 * Process the onlyBody param in url. * * 如果传参的时候设定了$onlyBody为真,或者当前页面请求中包含了onlybody=yes,在生成链接的时候继续追加。 * If $onlyBody set to true or onlybody=yes in the url, append onlyBody param to the link. * * @param string $link * @param bool $onlyBody * @static * @access public * @return string */ public static function processOnlyBodyParam($link, $onlyBody = false) { global $config; if(!$onlyBody and !self::inOnlyBodyMode()) return $link; $onlybodyString = $config->requestType != 'GET' ? "?onlybody=yes" : "&onlybody=yes"; return $link . $onlybodyString; } /** * 检查是否是onlybody模式。 * Check in only body mode or not. * * @access public * @return void */ public static function inOnlyBodyMode() { return (isset($_GET['onlybody']) and $_GET['onlybody'] == 'yes'); } /** * 使用helper::import()来引入文件,不要直接使用include或者require. * Using helper::import() to import a file, instead of include or require. * * @param string $file the file to be imported. * @static * @access public * @return bool */ static public function import($file) { $file = realpath($file); if(!is_file($file)) return false; static $includedFiles = array(); if(!isset($includedFiles[$file])) { include $file; $includedFiles[$file] = true; return true; } return true; } /** * 将数组或者列表转化成 IN( 'a', 'b') 的形式。 * Convert a list to IN('a', 'b') string. * * @param string|array $idList 列表,可以是数组或者用逗号隔开的列表。The id lists, can be a array or a string joined with comma. * @static * @access public * @return string the string like IN('a', 'b'). */ static public function dbIN($idList) { if(is_array($idList)) { if(!function_exists('get_magic_quotes_gpc') or !get_magic_quotes_gpc()) { foreach ($idList as $key=>$value) $idList[$key] = addslashes($value); } return "IN ('" . join("','", $idList) . "')"; } if(!function_exists('get_magic_quotes_gpc') or !get_magic_quotes_gpc()) $idList = addslashes($idList); return "IN ('" . str_replace(',', "','", str_replace(' ', '', $idList)) . "')"; } /** * 安全的Base64编码,框架对'/'字符比较敏感,转换为'.'。 * Create safe base64 encoded string for the framework. * * @param string $string the string to encode. * @static * @access public * @return string encoded string. */ static public function safe64Encode($string) { return strtr(base64_encode($string), '/', '.'); } /** * 解码base64,先将之前的'.' 转换回'/' * Decode the string encoded by safe64Encode. * * @param string $string the string to decode * @static * @access public * @return string decoded string. */ static public function safe64Decode($string) { return base64_decode(strtr($string, '.', '/')); } /** * JSON编码,自动处理转义的问题。 * JSON encode, process the slashes. * * @param mixed $data the object to encode * @static * @access public * @return string decoded string. */ static public function jsonEncode($data) { return (function_exists('get_magic_quotes_gpc') and get_magic_quotes_gpc()) ? addslashes(json_encode($data)) : json_encode($data); } /** * 判断是否是utf8编码 * Judge a string is utf-8 or not. * * @author hmdker@gmail.com * @param string $string * @see http://php.net/manual/en/function.mb-detect-encoding.php * @static * @access public * @return bool */ static public function isUTF8($string) { $c = 0; $b = 0; $bits = 0; $len = strlen($string); for($i=0; $i<$len; $i++) { $c = ord($string[$i]); if($c > 128) { if(($c >= 254)) return false; elseif($c >= 252) $bits=6; elseif($c >= 248) $bits=5; elseif($c >= 240) $bits=4; elseif($c >= 224) $bits=3; elseif($c >= 192) $bits=2; else return false; if(($i+$bits) > $len) return false; while($bits > 1) { $i++; $b=ord($string[$i]); if($b < 128 || $b > 191) return false; $bits--; } } } return true; } /** * 去掉UTF-8 Bom头。 * Remove UTF-8 Bom. * * @param string $string * @access public * @return string */ public static function removeUTF8Bom($string) { if(substr($string, 0, 3) == pack('CCC', 239, 187, 191)) return substr($string, 3); return $string; } /** * 增强substr方法:支持多字节语言,比如中文。 * Enhanced substr version: support multibyte languages like Chinese. * * @param string $string * @param int $length * @param string $append * @return string */ public static function substr($string, $length, $append = '') { $rawString = $string; if(function_exists('mb_substr')) $string = mb_substr($string, 0, $length, 'utf-8'); preg_match_all("/./su", $string, $data); $string = join("", array_slice($data[0], 0, $length)); return ($string != $rawString) ? $string . $append : $string; } /** * Get browser name and version. * * @access public * @return array */ public static function getBrowser() { $browser = array('name'=>'unknown', 'version'=>'unknown'); if(empty($_SERVER['HTTP_USER_AGENT'])) return $browser; $agent = $_SERVER["HTTP_USER_AGENT"]; /* Chrome should checked before safari.*/ if(strpos($agent, 'Firefox') !== false) $browser['name'] = "firefox"; if(strpos($agent, 'Opera') !== false) $browser['name'] = 'opera'; if(strpos($agent, 'Safari') !== false) $browser['name'] = 'safari'; if(strpos($agent, 'Chrome') !== false) $browser['name'] = "chrome"; /* Check the name of browser */ if(strpos($agent, 'MSIE') !== false || strpos($agent, 'rv:11.0')) $browser['name'] = 'ie'; if(strpos($agent, 'Edge') !== false) $browser['name'] = 'edge'; /* Check the version of browser */ if(preg_match('/MSIE\s(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; if(preg_match('/FireFox\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; if(preg_match('/Opera[\s|\/](\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; if(preg_match('/Chrome\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; if((strpos($agent, 'Chrome') == false) && preg_match('/Safari\/(\d+)\..*$/i', $agent, $regs)) $browser['version'] = $regs[1]; if(preg_match('/rv:(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; if(preg_match('/Edge\/(\d+)\..*/i', $agent, $regs)) $browser['version'] = $regs[1]; return $browser; } /** * Get client os from agent info. * * @static * @access public * @return string */ public static function getOS() { if(empty($_SERVER['HTTP_USER_AGENT'])) return 'unknow'; $osList = array(); $osList['/windows nt 10/i'] = 'Windows 10'; $osList['/windows nt 6.3/i'] = 'Windows 8.1'; $osList['/windows nt 6.2/i'] = 'Windows 8'; $osList['/windows nt 6.1/i'] = 'Windows 7'; $osList['/windows nt 6.0/i'] = 'Windows Vista'; $osList['/windows nt 5.2/i'] = 'Windows Server 2003/XP x64'; $osList['/windows nt 5.1/i'] = 'Windows XP'; $osList['/windows xp/i'] = 'Windows XP'; $osList['/windows nt 5.0/i'] = 'Windows 2000'; $osList['/windows me/i'] = 'Windows ME'; $osList['/win98/i'] = 'Windows 98'; $osList['/win95/i'] = 'Windows 95'; $osList['/win16/i'] = 'Windows 3.11'; $osList['/macintosh|mac os x/i'] = 'Mac OS X'; $osList['/mac_powerpc/i'] = 'Mac OS 9'; $osList['/linux/i'] = 'Linux'; $osList['/ubuntu/i'] = 'Ubuntu'; $osList['/iphone/i'] = 'iPhone'; $osList['/ipod/i'] = 'iPod'; $osList['/ipad/i'] = 'iPad'; $osList['/android/i'] = 'Android'; $osList['/blackberry/i'] = 'BlackBerry'; $osList['/webos/i'] = 'Mobile'; foreach ($osList as $regex => $value) { if(preg_match($regex, $_SERVER['HTTP_USER_AGENT'])) return $value; } return 'unknown'; } /** * 计算两个日期相差的天数,取整。 * Compute the diff days of two date. * * @param string $date1 the first date. * @param string $date2 the sencond date. * @access public * @return int the diff of the two days. */ static public function diffDate($date1, $date2) { return round((strtotime($date1) - strtotime($date2)) / 86400, 0); } /** * 获取当前时间,使用common语言文件定义的DT_DATETIME1常量。 * Get now time use the DT_DATETIME1 constant defined in the lang file. * * @access public * @return datetime now */ static public function now() { return date(DT_DATETIME1); } /** * 获取当前日期,使用common语言文件定义的DT_DATE1常量。 * Get today according to the DT_DATE1 constant defined in the lang file. * * @access public * @return date today */ static public function today() { return date(DT_DATE1); } /** * 获取当前日期,使用common语言文件定义的DT_TIME1常量。 * Get now time use the DT_TIME1 constant defined in the lang file. * * @access public * @return date today */ static public function time() { return date(DT_TIME1); } /** * 判断日期是不是零。 * Judge a date is zero or not. * * @access public * @return bool */ static public function isZeroDate($date) { return substr($date, 0, 4) == '0000'; } /** * 列出目录中符合该正则表达式的文件。 * Get files match the pattern under a directory. * * @access public * @return array the files match the pattern */ static public function ls($dir, $pattern = '') { if(empty($dir)) return array(); $files = array(); $dir = realpath($dir); if(is_dir($dir)) $files = glob($dir . DIRECTORY_SEPARATOR . '*' . $pattern); return empty($files) ? array() : $files; } /** * 切换目录。第一次调用的时候记录当前的路径,再次调用的时候切换回之前的路径。 * Change directory: first call, save the $cwd, secend call, change to $cwd. * * @param string $path * @static * @access public * @return void */ static function cd($path = '') { static $cwd = ''; if($path) $cwd = getcwd(); !empty($path) ? chdir($path) : chdir($cwd); } /** * 通过域名获取站点代号。 * Get siteCode for a domain. * * www.xirang.com => xirang * xirang.com => xirang * xirang.com.cn => xirang * xirang.cn => xirang * xirang => xirang * * @param string $domain * @return string $siteCode **/ public static function parseSiteCode($domain) { global $config; /* 去除域名中的端口部分。Remove the port part of the domain. */ if(strpos($domain, ':') !== false) $domain = substr($domain, 0, strpos($domain, ':')); $domain = strtolower($domain); /* $config里面有定义或者是localhost,直接返回。 Return directly if defined in $config or is localhost. */ if(isset($config->siteCodeList[$domain])) return $config->siteCodeList[$domain]; if($domain == 'localhost') return $domain; /* 将域名中的-改为_。Replace '-' with '_' in the domain. */ $domain = str_replace('-', '_', $domain); $items = explode('.', $domain); /* 类似a.com的形式。 Domain like a.com. */ $postfix = str_replace($items[0] . '.', '', $domain); if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[0]; /* 类似www.a.com的形式。 Domain like www.a.com. */ $postfix = str_replace($items[0] . '.' . $items[1] . '.', '', $domain); if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[1]; /* 类似xxx.sub.a.com的形式。 Domain like xxx.sub.a.com. */ $postfix = str_replace($items[0] . '.' . $items[1] . '.' . $items[2] . '.', '', $domain); if(isset($config->domainPostfix) and strpos($config->domainPostfix, "|$postfix|") !== false) return $items[0]; return ''; } /** * 检查是否是AJAX请求。 * Check is ajax request. * * @static * @access public * @return bool */ public static function isAjaxRequest() { if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true; if(isset($_GET['HTTP_X_REQUESTED_WITH']) && $_GET['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true; return false; } /** * 301跳转。 * Header 301 Moved Permanently. * * @param string $locate * @access public * @return void */ public static function header301($locate) { header('HTTP/1.1 301 Moved Permanently'); die(header('Location:' . $locate)); } /** * 获取远程IP。 * Get remote ip. * * @access public * @return string */ public static function getRemoteIp() { $ip = ''; if(!empty($_SERVER["REMOTE_ADDR"])) $ip = $_SERVER["REMOTE_ADDR"]; if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; if(!empty($_SERVER['HTTP_CLIENT_IP'])) $ip = $_SERVER['HTTP_CLIENT_IP']; return $ip; } } //------------------------------- 常用函数。Some tool functions.-------------------------------// /** * helper::createLink()的别名,方便创建本模块方法的链接。 * The short alias of helper::createLink() method to create link to control method of current module. * * @param string $methodName the method name * @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2) * @param string $viewType * @return string the link string. */ function inLink($methodName = 'index', $vars = '', $viewType = '') { global $app; return helper::createLink($app->getModuleName(), $methodName, $vars, $viewType); } /** * 通过一个静态游标,可以遍历数组。 * Static cycle a array. * * @param array $items the array to be cycled. * @return mixed */ function cycle($items) { static $i = 0; if(!is_array($items)) $items = explode(',', $items); if(!isset($items[$i])) $i = 0; return $items[$i++]; } /** * 获取当前时间的Unix时间戳,精确到微妙。 * Get current microtime. * * @access public * @return float current time. */ function getTime() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } /** * 打印变量的信息 * dump a var. * * @param mixed $var * @access public * @return void */ function a($var) { echo ""; print_r($var); echo ""; } /** * 判断是否内外IP。 * Judge the server ip is local or not. * * @access public * @return void */ function isLocalIP() { global $config; if(isset($config->islocalIP)) return $config->isLocalIP; $serverIP = $_SERVER['SERVER_ADDR']; if($serverIP == '127.0.0.1') return true; if(strpos($serverIP, '10.70') !== false) return false; return !filter_var($serverIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); } /** * 获取webRoot。 * Get web root. * * @access public * @return string */ function getWebRoot($full = false) { $path = $_SERVER['SCRIPT_NAME']; if(PHP_SAPI == 'cli') { if(isset($_SERVER['argv'][1])) { $url = parse_url($_SERVER['argv'][1]); $path = empty($url['path']) ? '/' : rtrim($url['path'], '/'); } $path = empty($path) ? '/' : preg_replace('/\/www$/', '/www/', $path); } if($full) { $http = (isset($_SERVER['HTTPS']) and strtolower($_SERVER['HTTPS']) != 'off') ? 'https://' : 'http://'; return $http . $_SERVER['HTTP_HOST'] . substr($path, 0, (strrpos($path, '/') + 1)); } $path = substr($path, 0, (strrpos($path, '/') + 1)); $path = str_replace('\\', '/', $path); return $path; } /** * 当数组/对象变量$var存在$key项时,返回存在的对应值或设定值,否则返回$key或不存在的设定值。 * When the $var has the $key, return it, esle result one default value. * * @param array|object $var * @param string|int $key * @param mixed $valueWhenNone value when the key not exits. * @param mixed $valueWhenExists value when the key exits. * @access public * @return string */ function zget($var, $key, $valueWhenNone = false, $valueWhenExists = false) { if(!is_array($var) and !is_object($var)) return false; $type = is_array($var) ? 'array' : 'object'; $checkExists = $type == 'array' ? isset($var[$key]) : isset($var->$key); if($checkExists) { if($valueWhenExists !== false) return $valueWhenExists; return $type == 'array' ? $var[$key] : $var->$key; } if($valueWhenNone !== false) return $valueWhenNone; return $key; } ================================================ FILE: framework/base/model.class.php ================================================ app. * 2. set the pathes, config, lang of current module * * @param string $appName * @access public * @return void */ public function __construct($appName = '') { global $app, $config, $lang, $dbh; $this->app = $app; $this->config = $config; $this->lang = $lang; $this->dbh = $dbh; $this->appName = empty($appName) ? $this->app->getAppName() : $appName; $moduleName = $this->getModuleName(); if($this->config->framework->multiLanguage) $this->app->loadLang($moduleName, $this->appName); if($moduleName != 'common') $this->app->loadModuleConfig($moduleName, $this->appName); $this->loadDAO(); $this->setSuperVars(); } /** * 获取该model的模块名,而不是用户请求的模块名。 * * 这个方法通过去掉该model类名的'ext'和'model'字符串,来获取当前模块名。 * 不要使用$app->getModuleName(),因为其返回的是用户请求的模块名。 * 另一个model可以通过loadModel()加载进来,与请求的模块名不一致。 * * Get the module name of this model. Not the module user visiting. * * This method replace the 'ext' and 'model' string from the model class name, thus get the module name. * Not using $app->getModuleName() because it return the module user is visiting. But one module can be * loaded by loadModel() so we must get the module name of this model. * * @access public * @return string the module name. */ public function getModuleName() { $parentClass = get_parent_class($this); $selfClass = get_class($this); $className = $parentClass == 'model' ? $selfClass : $parentClass; if($className == 'extensionModel') return 'extension'; return strtolower(str_ireplace(array('ext', 'Model'), '', $className)); } /** * 设置全局超级变量。 * Set the super vars. * * @access public * @return void */ public function setSuperVars() { $this->post = $this->app->post; $this->get = $this->app->get; $this->server = $this->app->server; $this->cookie = $this->app->cookie; $this->session = $this->app->session; } /** * 加载一个模块的model。加载完成后,使用$this->$moduleName来访问这个model对象。 * 比如:loadModel('user')引入user模块的model实例对象,可以通过$this->user来访问它。 * * Load the model of one module. After loaded, can use $this->$moduleName to visit the model object. * * @param string $moduleName * @access public * @return object|bool the model object or false if model file not exists. */ public function loadModel($moduleName, $appName = '') { if(empty($moduleName)) return false; if(empty($appName)) $appName = $this->appName; global $loadedModels; if(isset($loadedModels[$appName][$moduleName])) { $this->$moduleName = $loadedModels[$appName][$moduleName]; return $this->$moduleName; } $modelFile = $this->app->setModelFile($moduleName, $appName); if(!helper::import($modelFile)) return false; $modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; if(!class_exists($modelClass)) { $modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); } $loadedModels[$appName][$moduleName] = new $modelClass($appName); $this->$moduleName = $loadedModels[$appName][$moduleName]; return $this->$moduleName; } /** * 加载model的class扩展,主要是为了开发加密代码使用。 * 可以将主要的逻辑存放到$moduleName/ext/model/class/$extensionName.class.php中。 * 然后在ext/model/$extension.php的扩展里面使用$this->loadExtension()来调用相应的方法。 * ext/model/class/*.class.php代码可以加密。而ext/model/*.php可以不用加密。 * 因为框架对model的扩展是采取合并文件的方式,ext/model/*.php文件不能加密。 * * Load extension class of a model thus user can encrypt the code. * You can put the main extension logic codes in $moduleName/ext/model/class/$extensionName.class.php. * And call them by the ext/model/$extension.php like this: $this->loadExtension('myextension')->method(). * You can encrypt the code in ext/model/class/*.class.php. * Because the framework will merge the extension files in ext/model/*.php to the module/model.php. * * @param string $extensionName * @param string $moduleName * @access public * @return void */ public function loadExtension($extensionName, $moduleName = '') { if(empty($extensionName)) return false; /* 设置扩展的名字和相应的文件。Set extenson name and extension file. */ $extensionName = strtolower($extensionName); $moduleName = $moduleName ? $moduleName : $this->getModuleName(); $moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'model'); if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php'; if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php'; /* 载入父类。Try to import parent model file auto and then import the extension file. */ if(!class_exists($moduleName . 'Model')) helper::import($this->app->getModulePath($this->appName, $moduleName) . 'model.php'); if(!helper::import($extensionFile)) return false; /* 设置扩展类的名字。Set the extension class name. */ $extensionClass = $extensionName . ucfirst($moduleName); if(!class_exists($extensionClass)) return false; /* 实例化扩展类。Create an instance of the extension class and return it. */ $extensionObject = new $extensionClass; $extensionClass = str_replace('Model', '', $extensionClass); $this->$extensionClass = $extensionObject; return $extensionObject; } /** * 加载DAO。 * Load DAO. * * @access public * @return void */ public function loadDAO() { $this->dao = $this->app->loadClass('dao'); } /** * 删除记录。 * Delete one record. * * @param string $table the table name * @param string $id the id value of the record to be deleted * @access public * @return void */ public function delete($table, $id = 0) { $this->dao->delete()->from($table)->where('id')->eq($id)->exec(); } } ================================================ FILE: framework/base/router.class.php ================================================ basePath/framework) * * @var string * @access public */ public $frameRoot; /** * 类库的根目录。{$this->basePath/lib} * The root directory of the library($this->basePath/lib). * * @var string * @access public */ public $coreLibRoot; /** * 应用名称 * The appName. * * @var string * @access public */ public $appName = ''; /** * 应用程序的根目录。 * The root directory of the app. * * @var string * @access public */ public $appRoot; /** * 临时文件的根目录。 * The root directory of temp. * * @var string * @access public */ public $tmpRoot; /** * 缓存的根目录。 * The root directory of cache. * * @var string * @access public */ public $cacheRoot; /** * WWW目录。 * The root directory of www. * * @var string * @access public */ public $wwwRoot; /** * 附件存放目录。 * The root directory of data. * * @var string * @access public */ public $dataRoot; /** * 日志文件的根目录。 * The root directory of log. * * @var string * @access public */ public $logRoot; /** * 配置文件的根目录。 * The root directory of config. * * @var string * @access public */ public $configRoot; /** * 模块的根目录。 * The root directory of module. * * @var string * @access public */ public $moduleRoot; /** * 主题的根目录。 * The root directory of theme. * * @var string * @access public */ public $themeRoot; /** * 用户使用的语言。 * The lang of the client user. * * @var string * @access public */ public $clientLang; /** * 用户使用的主题。 * The theme of the client user. * * @var string * @access public */ public $clientTheme; /** * 客户端设备类型。 * The device type of client. * * @var string * @access public */ public $clientDevice; /** * 当前模块的control对象。 * The control object of current module. * * @var object * @access public */ public $control; /** * 模块名。 * The module name * * @var string * @access public */ public $moduleName; /** * 当前访问模块的control文件。 * The control file of the module current visiting. * * @var string * @access public */ public $controlFile; /** * 当前访问的方法名。 * The name of the method current visiting. * * @var string * @access public */ public $methodName; /** * 当前方法的扩展文件。 * The action extension file of current method. * * @var string * @access public */ public $extActionFile; /** * 访问的URI。 * The URI. * * @var string * @access public */ public $URI; /** * url地址传递的参数。 * The params passed in through url. * * @var array * @access public */ public $params; /** * 视图类型。 * The view type. * * @var string * @access public */ public $viewType; /** * 全局$config对象。 * The global $config object. * * @var object * @access public */ public $config; /** * 全局$lang对象。 * The global $lang object. * * @var object * @access public */ public $lang; /** * 全局$dbh对象,数据库连接句柄。 * The global $dbh object, the database connection handler. * * @var object * @access public */ public $dbh; /** * 从数据库的句柄。 * The slave database handler. * * @var object * @access public */ public $slaveDBH; /** * $post对象,用于访问$_POST变量。 * The $post object, used to access the $_POST var. * * @var object * @access public */ public $post; /** * $get对象,用于访问$_GET变量。 * The $get object, used to access the $_GET var. * * @var object * @access public */ public $get; /** * $session对象,用于访问$_SESSION变量。 * The $session object, used to access the $_SESSION var. * * @var object * @access public */ public $session; /** * $server对象,用于访问$_SERVER变量。 * The $server object, used to access the $_SERVER var. * * @var object * @access public */ public $server; /** * $cookie对象,用于访问$_COOKIE变量。 * The $cookie object, used to access the $_COOKIE var. * * @var object * @access public */ public $cookie; /** * 网站代号。 * The code of current site. * * @var string * @access public */ public $siteCode; /** * 构造方法, 设置路径,类,超级变量等。注意: * 1.应该使用createApp()方法实例化router类; * 2.如果$appRoot为空,框架会根据$appName计算应用路径。 * * The construct function. * Prepare all the paths, classes, super objects and so on. * Notice: * 1. You should use the createApp() method to get an instance of the router. * 2. If the $appRoot is empty, the framework will compute the appRoot according the $appName * * @param string $appName the name of the app * @param string $appRoot the root path of the app * @access public * @return void */ public function __construct($appName = 'demo', $appRoot = '') { $this->setPathFix(); $this->setBasePath(); $this->setFrameRoot(); $this->setCoreLibRoot(); $this->setAppRoot($appName, $appRoot); $this->setTmpRoot(); $this->setCacheRoot(); $this->setLogRoot(); $this->setConfigRoot(); $this->setModuleRoot(); $this->setWwwRoot(); $this->setThemeRoot(); $this->setDataRoot(); $this->loadMainConfig(); $this->loadClass('front', $static = true); $this->loadClass('filter', $static = true); $this->loadClass('dao', $static = true); $this->loadClass('mobile', $static = true); $this->setSuperVars(); $this->setDebug(); $this->setErrorHandler(); $this->setTimezone(); $this->startSession(); if($this->config->framework->multiSite) $this->setSiteCode() && $this->loadExtraConfig(); if($this->config->framework->autoConnectDB) $this->connectDB(); if($this->config->framework->multiLanguage) $this->setClientLang(); $needDetectDevice = zget($this->config->framework->detectDevice, $this->clientLang, false); $this->clientDevice = $needDetectDevice ? $this->setClientDevice() : 'desktop'; if($this->config->framework->multiLanguage) $this->loadLang('common'); if($this->config->framework->multiTheme) $this->setClientTheme(); } /** * 创建一个应用。 * Create an application. * * @param string $appName 应用名称。 The name of the app. * @param string $appRoot 应用根路径。The root path of the app. * @param string $className 应用类名,如果对router类做了扩展,需要指定类名。When extends router class, you should pass in the child router class name. * @static * @access public * @return object the app object */ public static function createApp($appName = 'demo', $appRoot = '', $className = '') { if(empty($className)) $className = __CLASS__; return new $className($appName, $appRoot); } //-------------------- 路径相关方法(Path related methods)--------------------// /** * 设置应用名称。 * Set app name. * * @param string $appName * @access public * @return void */ public function setAppName($appName) { $this->appName = $appName; } /** * 设置目录分隔符。 * Set the path directory separator. * * @access public * @return void */ public function setPathFix() { define('DS', DIRECTORY_SEPARATOR); } /** * 设置基础目录。 * Set the base path. * * @access public * @return void */ public function setBasePath() { $this->basePath = realpath(dirname(dirname(dirname(__FILE__)))) . DS; } /** * 设置框架根目录。 * Set the frame root. * * @access public * @return void */ public function setFrameRoot() { $this->frameRoot = $this->basePath . 'framework' . DS; } /** * 设置类库的根目录。 * Set the app lib root. * * @access public * @return void */ public function setCoreLibRoot() { $this->coreLibRoot = $this->basePath . 'lib' . DS; } /** * 设置应用的根目录。 * Set the app root. * * @param string $appName * @param string $appRoot * @access public * @return void */ public function setAppRoot($appName = 'demo', $appRoot = '') { if(empty($appRoot)) $this->appRoot = $this->basePath . 'app' . DS . $appName . DS; if(!empty($appRoot)) $this->appRoot = realpath($appRoot) . DS; if(!is_dir($this->appRoot)) $this->triggerError("The app you call not found in {$this->appRoot}", __FILE__, __LINE__, $exit = true); } /** * 设置临时文件的根目录。 * Set the tmp root. * * @access public * @return void */ public function setTmpRoot() { $this->tmpRoot = $this->basePath . 'tmp' . DS; } /** * 设置缓存的根目录。 * Set the cache root. * * @access public * @return void */ public function setCacheRoot() { $this->cacheRoot = $this->tmpRoot . 'cache' . DS; } /** * 设置log的根目录。 * Set the log root. * * @access public * @return void */ public function setLogRoot() { $this->logRoot = $this->tmpRoot . 'log' . DS; } /** * 设置config配置文件的根目录。 * Set the config root. * * @access public * @return void */ public function setConfigRoot() { $this->configRoot = $this->basePath . 'config' . DS; } /** * 设置模块的根目录。 * Set the module root. * * @access public * @return void */ public function setModuleRoot() { $this->moduleRoot = $this->basePath . 'module' . DS; } /** * 设置www的根目录。 * Set the www root. * * @access public * @return void */ public function setWwwRoot() { $this->wwwRoot = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), DS) . DS; } /** * 设置主题根目录。 * Set the theme root. * * @access public * @return void */ public function setThemeRoot() { $this->themeRoot = $this->wwwRoot . 'theme' . DS; } /** * 设置data根目录。 * Set the data root. * * @access public * @return void */ public function setDataRoot() { $this->dataRoot = $this->wwwRoot . 'data' . DS; } /** * 设置超级变量。 * Set the super vars. * * @access public * @return void */ public function setSuperVars() { $this->post = new super('post'); $this->get = new super('get'); $this->server = new super('server'); $this->cookie = new super('cookie'); $this->session = new super('session'); unset($GLOBALS); unset($_REQUEST); $_FILES = validater::filterFiles(); $_POST = validater::filterSuper($_POST); $_GET = validater::filterSuper($_GET); $_COOKIE = validater::filterSuper($_COOKIE); } /** * 设置Debug模式。 * set Debug. * * @access public * @return void */ public function setDebug() { if(!empty($this->config->debug)) error_reporting(E_ALL & ~ E_STRICT); } /** * 设置错误处理句柄。 * Set the error handler. * * @access public * @return void */ public function setErrorHandler() { set_error_handler(array($this, 'saveError')); register_shutdown_function(array($this, 'shutdown')); } /** * 获取应用名称 * Get app name * * @access public * @return string */ public function getAppName() { return $this->appName; } /** * 获取$basePath,即基础路径。 * Get the $basePath var. * * @access public * @return string */ public function getBasePath() { return $this->basePath; } /** * 获取$frameRoot,即框架根目录。 * Get the $frameRoot var. * * @access public * @return string */ public function getFrameRoot() { return $this->frameRoot; } /** * 获取$appRoot变量,即应用的根目录。 * Get the $appRoot var. * * @access public * @return string */ public function getAppRoot() { return $this->appRoot; } /** * 获取$wwwRoot变量。 * Get the $wwwRoot var * * @access public * @return string */ public function getWwwRoot() { return $this->wwwRoot; } /** * 获取$coreLibRoot变量,即应用类库的根目录。 * Get the $coreLibRoot var. * * @access public * @return string */ public function getCoreLibRoot() { return $this->coreLibRoot; } /** * 获取$tmpRoot变量,即临时文件的根目录。 * Get the $tmpRoot var. * * @access public * @return string */ public function getTmpRoot() { return $this->tmpRoot; } /** * 获取$cacheRoot变量,即缓存文件的根目录。 * Get the $cacheRoot var. * * @access public * @return string */ public function getCacheRoot() { return $this->cacheRoot; } /** * 获取$logRoot变量,即日志文件的根目录。 * Get the $logRoot var. * * @access public * @return string */ public function getLogRoot() { return $this->logRoot; } /** * 获取$configRoot变量,即配置文件的根目录。 * Get the $configRoot var. * * @access public * @return string */ public function getConfigRoot() { return $this->configRoot; } /** * 获取$moduleRoot变量,即应用模块的根目录。 * Get the $moduleRoot var. * * @param string $appName * @access public * @return string */ public function getModuleRoot($appName = '') { if($appName == '') return $this->moduleRoot; return dirname($this->moduleRoot) . DS . $appName . DS; } /** * 获取$webRoot,即应用的路径。 * Get the $webRoot var. * * @access public * @return string */ public function getWebRoot() { return $this->config->webRoot; } /** * 获取$themeRoot变量,即主题的根目录。 * Get the $themeRoot var. * * @access public * @return string */ public function getThemeRoot() { return $this->themeRoot; } /** * 获取$dataRoot目录 * Get the $dataRoot var * * @access public * @return string */ public function getDataRoot() { return $this->dataRoot; } //------ 客户端环境有关的函数(Client environment related functions) ------// /** * 根据配置设置当前时区。 * Set the time zone according to the config. * * @access public * @return void */ public function setTimezone() { if(isset($this->config->timezone)) date_default_timezone_set($this->config->timezone); } /** * 开启 session * Start the session. * * @access public * @return void */ public function startSession() { if(!defined('SESSION_STARTED')) { $sessionName = $this->config->sessionVar; session_name($sessionName); if(isset($_GET[$this->config->sessionVar])) session_id($_GET[$this->config->sessionVar]); session_start(); define('SESSION_STARTED', true); } } /** * 根据用户浏览器的语言设置和服务器配置,选择显示的语言。 * 优先级:$lang参数 > session > cookie > 浏览器 > 配置文件。 * * Set the language. * Using the order of method $lang param, session, cookie, browser and the default lang. * * @param string $lang zh-cn|zh-tw|zh-hk|en * @access public * @return void */ public function setClientLang($lang = '') { if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $this->clientLang = $this->parseHttpAcceptLang(); if(isset($_COOKIE['lang'])) $this->clientLang = $_COOKIE['lang']; if(isset($_SESSION['lang'])) $this->clientLang = $_SESSION['lang']; if(!empty($lang)) $this->clientLang = $lang; if(!empty($this->clientLang)) { $this->clientLang = strtolower($this->clientLang); if(!isset($this->config->langs[$this->clientLang])) $this->clientLang = $this->config->default->lang; } else { $this->clientLang = $this->config->default->lang; } setcookie('lang', $this->clientLang, $this->config->cookieLife, $this->config->webRoot); if(!isset($_COOKIE['lang'])) $_COOKIE['lang'] = $this->clientLang; return true; } /** * 从HTTP_ACCEPT_LANGUAGE中提出去支持的语言。 * Parse the lang str from HTTP_ACCEPT_LANGUAGE header. * * @access public * @return string */ public function parseHttpAcceptLang() { if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) return ''; $raw = $_SERVER['HTTP_ACCEPT_LANGUAGE']; $pos = strpos($raw, ','); $lang = $pos === false ? $raw : substr($raw, 0, $pos); /* Fix clientLang for ie >= 10. https://www.drupal.org/node/365615. */ if(stripos($lang, 'hans')) $lang = 'zh-cn'; if(stripos($lang, 'hant')) $lang = 'zh-tw'; return $lang; } /** * 设置客户端使用的主题,判断逻辑与客户端的语言相同。 * 主题的css和图片文件应该存放在www/theme/$themeName路径。 * * Set the theme the client user using. The logic is same as the clientLang. * The css and images files of an theme should saved at www/theme/$themeName * * @param string $theme * @access public * @return void */ public function setClientTheme($theme = '') { if(isset($this->config->client->theme)) $this->clientTheme = $this->config->client->theme; if(isset($_COOKIE['theme'])) $this->clientTheme = $_COOKIE['theme']; if(!empty($theme)) $this->clientTheme = $theme; if(!empty($this->clientTheme)) { $this->clientTheme = strtolower($this->clientTheme); if(!isset($this->lang->themes[$this->clientTheme])) $this->clientTheme = $this->config->default->theme; } else { $this->clientTheme = $this->config->default->theme; } setcookie('theme', $this->clientTheme, $this->config->cookieLife, $this->config->webRoot); if(!isset($_COOKIE['theme'])) $_COOKIE['theme'] = $this->clientTheme; return true; } /** * 设置客户端的设备类型。 * Set client device. * * @access public * @return void */ public function setClientDevice() { $this->clientDevice = 'desktop'; if($this->cookie->device == 'mobile') $this->clientDevice = 'mobile'; if($this->cookie->device == 'desktop') $this->clientDevice = 'desktop'; if(strpos('mobile,desktop', $this->cookie->device) === false) { $mobile = new mobile(); $this->clientDevice = ($mobile->isMobile() and !$mobile->isTablet()) ? 'mobile' : 'desktop'; } setcookie('device', $this->clientDevice, $this->config->cookieLife, $this->config->webRoot); if(!isset($_COOKIE['device'])) $_COOKIE['device'] = $this->clientDevice; return $this->clientDevice; } /** * 设置站点代号,可以针对不同的站点来加载不同的扩展。 * Set the code of current site, thus can load diffrent extension of diffrent site. * * @access public * @return void */ public function setSiteCode() { return $this->siteCode = helper::parseSiteCode($this->server->http_host); } /** * 获取$clientLang变量,即客户端的语言。 * Get the $clientLang var. * * @access public * @return string */ public function getClientLang() { return $this->clientLang; } /** * 获取$clientTheme变量。 * Get the $clientTheme var. * * @access public * @return string */ public function getClientTheme() { return $this->config->webRoot . 'theme/' . $this->clientTheme . '/'; } /** * 获得客户端的终端设备。 * Get the client device. * * @access public * @return void */ public function getClientDevice() { return $this->clientDevice; } //-------------------- 请求相关的方法(Request related methods) --------------------// /** * 解析本次请求的入口方法,根据请求的类型(PATH_INFO GET),调用相应的方法。 * The entrance of parseing request. According to the requestType, call related methods. * * @access public * @return void */ public function parseRequest() { if($this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2') { $this->parsePathInfo(); $this->setRouteByPathInfo(); } elseif($this->config->requestType == 'GET') { $this->parseGET(); $this->setRouteByGET(); } else { $this->triggerError("The request type {$this->config->requestType} not supported", __FILE__, __LINE__, $exit = true); } } /** * PATH_INFO方式解析,获取$URI和$viewType。 * Parse PATH_INFO, get the $URI and $viewType. * * @access public * @return void */ public function parsePathInfo() { $pathInfo = $this->getPathInfo(); if(!empty($pathInfo)) { $dotPos = strrpos($pathInfo, '.'); if($dotPos) { $this->URI = substr($pathInfo, 0, $dotPos); $this->viewType = substr($pathInfo, $dotPos + 1); if(strpos($this->config->views, ',' . $this->viewType . ',') === false) { $this->viewType = $this->config->default->view; } } else { $this->URI = $pathInfo; $this->viewType = $this->config->default->view; } } else { $this->viewType = $this->config->default->view; } } /** * 从$_SERVER或者$_ENV全局变量根据pathinfo变量名获取$PATH_INFO值。 * PATH_INFO的变量名几乎都是'PATH_INFO',但也有可能是ORIG_PATH_INFO。 * * Get $PATH_INFO from $_SERVER or $_ENV by the pathinfo var name. * Mostly, the var name of PATH_INFO is PATH_INFO, but may be ORIG_PATH_INFO. * * @access public * @return string the PATH_INFO */ public function getPathInfo() { if(isset($_SERVER['PATH_INFO'])) { $value = $_SERVER['PATH_INFO']; } elseif(isset($_SERVER['ORIG_PATH_INFO'])) { $value = $_SERVER['ORIG_PATH_INFO']; } elseif(isset($this->URI)) { $value = $this->URI; $subpath = str_replace($_SERVER['DOCUMENT_ROOT'], '', dirname($_SERVER['SCRIPT_FILENAME'])); if($subpath != '/') $subpath = '/' . $subpath; if($subpath != '' and $subpath != '/' and strpos($value, $subpath) === 0) $value = substr($value, strlen($subpath)); } else { $value = @getenv('PATH_INFO'); if(empty($value)) $value = @getenv('ORIG_PATH_INFO'); } if(strpos($value, $_SERVER['SCRIPT_NAME']) !== false) $value = str_replace($_SERVER['SCRIPT_NAME'], '', $value); if(strpos($value, '?') === false) return trim($value, '/'); $value = parse_url($value); $pathInfo = trim(zget($value, 'path', ''), '/'); if(trim($pathInfo, '/') == trim($this->config->webRoot, '/')) $pathInfo = ''; return $pathInfo; } /** * GET请求方式解析,获取$URI和$viewType。 * Parse GET, get $URI and $viewType. * * @access public * @return void */ public function parseGET() { if(isset($_GET[$this->config->viewVar])) { $this->viewType = $_GET[$this->config->viewVar]; if(strpos($this->config->views, ',' . $this->viewType . ',') === false) $this->viewType = $this->config->default->view; } else { $this->viewType = $this->config->default->view; } $this->URI = $_SERVER['REQUEST_URI']; } /** * 获取$URL。 * Get the $URL. * * @param bool $full true, the URI contains the webRoot, else only hte URI. * @access public * @return string */ public function getURI($full = false) { if($full and $this->config->requestType == 'PATH_INFO') { if($this->URI) return $this->config->webRoot . $this->URI . '.' . $this->viewType; return $this->config->webRoot; } return $this->URI; } /** * 获取$vewType变量。 * Get the $viewType var. * * @access public * @return string */ public function getViewType() { return $this->viewType; } //-------------------- 模块及扩展设置(Module and extension) --------------------// /** * 加载common模块。 * * common模块比较特别,它会执行几乎每次请求都需要执行的操作,例如: * 打开session,检查权限等等。 * 加载完$lang, $config, $dbh后,需要在入口文件(www/index.php)中手动调用该方法。 * * Load the common module * * The common module is a special module, which can be used to do some common things. For examle: * start session, check priviledge and so on. * This method should called manually in the router file(www/index.php) after the $lang, $config, $dbh loaded. * * @access public * @return object|bool the common model object or false if not exits. */ public function loadCommon() { $this->setModuleName('common'); $commonModelFile = $this->setModelFile('common'); if(!file_exists($commonModelFile)) return false; helper::import($commonModelFile); if($this->config->framework->extensionLevel == 0 and class_exists('commonModel')) return new commonModel(); if($this->config->framework->extensionLevel > 0 and class_exists('extCommonModel')) return new extCommonModel(); if(class_exists('commonModel')) return new commonModel(); return false; } /** * 设置要被调用的模块名。 * Set the name of the module to be called. * * @param string $moduleName the module name * @access public * @return void */ public function setModuleName($moduleName = '') { if($this->checkModuleName($moduleName)) $this->moduleName = strtolower($moduleName); } /** * 设置要被调用的控制器文件。 * Set the control file of the module to be called. * * @param bool $exitIfNone 没有找到该控制器文件的情况:如果该参数为true,则终止程序;如果为false,则打印错误日志 * If control file not foundde, how to do. True, die the whole app. false, log error. * @access public * @return bool */ public function setControlFile($exitIfNone = true) { $this->controlFile = $this->moduleRoot . $this->moduleName . DS . 'control.php'; if(file_exists($this->controlFile)) return true; $this->triggerError("the control file $this->controlFile not found.", __FILE__, __LINE__, $exitIfNone); } /** * 设置要被调用的方法名。 * Set the name of the method calling. * * @param string $methodName * @access public * @return void */ public function setMethodName($methodName = '') { if($this->checkMethodName($methodName)) $this->methodName = strtolower($methodName); } /** * 获取一个模块的路径。 * Get the path of one module. * * @param string $appName the app name * @param string $moduleName the module name * @access public * @return string the module path */ public function getModulePath($appName = '', $moduleName = '') { if($moduleName == '') $moduleName = $this->moduleName; if($this->checkModuleName($moduleName)) { $modulePath = $this->getModuleRoot($appName) . strtolower($moduleName) . DS; return $modulePath; } } /** * 获取一个模块的扩展路径。 Get extension path of one module. * * If the extensionLevel == 0, return empty array. * If the extensionLevel == 1, return the common extension directory. * If the extensionLevel == 2, return the common and site extension directories. * * @param string $appName the app name * @param string $moduleName the module name * @param string $ext the extension type, can be control|model|view|lang|config * @access public * @return string the extension path. */ public function getModuleExtPath($appName, $moduleName, $ext) { /* 检查失败或者extensionLevel为0,直接返回空。If check failed or extensionLevel == 0, return empty array. */ if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array(); /* When extensionLevel == 1. */ $modulePath = $this->getModulePath($appName, $moduleName); $paths = array(); $paths['common'] = $modulePath . 'ext' . DS . $ext . DS; if($this->config->framework->extensionLevel == 1) return $paths; /* When extensionLevel == 2. */ $paths['site'] = empty($this->siteCode) ? '' : $modulePath . 'ext' . DS . '_' . $this->siteCode . DS . $ext . DS; return $paths; } /** * 检查模块中某一个变量必须为英文字母和数字组合。Check module a variable must be ascii. * * @param string $var * @access public * @return bool */ public function checkModuleName($var) { global $filter; $rule = $filter->default->moduleName; if(validater::checkByRule($var, $rule)) return true; $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true); } /** * 检查方法中某一个变量必须为英文字母和数字组合。Check method a variable must be ascii. * * @param string $var * @access public * @return bool */ public function checkMethodName($var) { global $filter; $rule = $filter->default->methodName; if($this->config->framework->filterParam == 2 and isset($filter->{$this->moduleName}->methodName)) $rule = $filter->{$this->moduleName}->methodName; if(validater::checkByRule($var, $rule)) return true; $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true); } /** * 设置Action的扩展文件。 Set the action extension file. * * @access public * @return bool */ public function setActionExtFile() { $moduleExtPaths = $this->getModuleExtPath('', $this->moduleName, 'control'); /* 如果扩展目录为空,不包含任何扩展文件。If there's no ext pathes return false.*/ if(empty($moduleExtPaths)) return false; /* 如果extensionLevel == 2,且扩展文件存在,返回该站点扩展文件。If extensionLevel == 2 and site extensionFile exists, return it. */ if($this->config->framework->extensionLevel == 2 and !empty( $moduleExtPaths['site'])) { $this->extActionFile = $moduleExtPaths['site'] . $this->methodName . '.php'; if(file_exists($this->extActionFile)) return true; } /* 然后再尝试寻找公共扩展文件。Then try to find the common extension file. */ $this->extActionFile = $moduleExtPaths['common'] . $this->methodName . '.php'; return file_exists($this->extActionFile); } /** * 设置一个模块的model文件,如果存在model扩展,一起合并。 * Set the model file of one module. If there's an extension file, merge it with the main model file. * * @param string $moduleName the module name * @param string $appName the app name * @static * @access public * @return string the model file */ public function setModelFile($moduleName, $appName = '') { if($appName == '') $appName = $this->getAppName(); /* 设置主model文件。 Set the main model file. */ $mainModelFile = $this->getModulePath($appName, $moduleName) . 'model.php'; if($this->config->framework->extensionLevel == 0) return $mainModelFile; /* 计算扩展的文件和hook文件。Compute the extension files and hook files. */ $hookFiles = array(); $extFiles = array(); $siteExtended = false; $modelExtPaths = $this->getModuleExtPath($appName, $moduleName, 'model'); foreach($modelExtPaths as $extType => $modelExtPath) { if(empty($modelExtPath)) continue; $tmpHookFiles = helper::ls($modelExtPath . 'hook/', '.php'); $tmpExtFiles = helper::ls($modelExtPath, '.php'); $hookFiles = array_merge($hookFiles, $tmpHookFiles); $extFiles = array_merge($extFiles, $tmpExtFiles); if($extType == 'site' and (!empty($tmpHookFiles) or !empty($tmpExtFiles))) $siteExtended = true; } /* 如果没有扩展文件,返回主文件。 If no extension or hook files, return the main file directly. */ if(empty($extFiles) and empty($hookFiles)) return $mainModelFile; /* 计算合并之后的modelFile路径。Compute the merged model file path. */ $extModelPrefix = ($siteExtended and !empty($this->siteCode)) ? $this->siteCode{0} . DS . $this->siteCode : ''; $mergedModelDir = $this->getTmpRoot() . 'model' . DS . ($extModelPrefix ? $extModelPrefix . DS : ''); $mergedModelFile = $mergedModelDir . $moduleName . '.php'; if(!is_dir($mergedModelDir)) mkdir($mergedModelDir, 0755, true); /* 判断生成的缓存文件是否需要更新。 Judge whether the merged model file needed update or not. */ if(!$this->needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile)) return $mergedModelFile; /* 合并扩展和hook文件。Merge the extension and hook files. */ $modelLines = $this->mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir); $this->mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile); return $mergedModelFile; } /** * 检查合并之后的model文件是否需要更新。Check whether the merged model file need update or not. * * @param string $mainModelFile * @param string $mergedModelFile * @param string $modelExtPaths * @param array $extFiles * @param array $hookFiles * @access public * @return bool */ public function needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile) { $lastTime = file_exists($mergedModelFile) ? filemtime($mergedModelFile) : 0; foreach($extFiles as $extFile) if(filemtime($extFile) > $lastTime) return true; foreach($hookFiles as $hookFile) if(filemtime($hookFile) > $lastTime) return true; $modelExtPath = $modelExtPaths['common']; $modelHookPath = $modelExtPaths['common'] . 'hook/'; if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; if(!empty($modelExtPaths['site'])) { $modelExtPath = $modelExtPaths['site']; $modelHookPath = $modelExtPaths['site'] . 'hook/'; if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; } if(filemtime($mainModelFile) > $lastTime) return true; return false; } /** * 将model的扩展文件合并在一起。Merge model ext files. * * @param string $moduleName * @param string $mainModelFile * @param array $extFiles * @param string $mergedModelDir * @access public * @return void */ public function mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir) { /* 设置类名。Set the class names. */ $modelClass = "{$moduleName}Model"; $tmpModelClass = "tmpExt$modelClass"; /* 开始拼装代码。Prepare the codes. */ $modelLines = "getBasePath());\n"; $modelLines .= "helper::import('" . str_replace($this->getBasePath(), '', $mainModelFile) . "');\n"; $modelLines .= "helper::cd();\n"; $modelLines .= "class $tmpModelClass extends $modelClass \n{\n"; /* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into model lines. */ foreach($extFiles as $extFile) $modelLines .= self::removePHPTAG($extFile); /* 做个标记,方便后面替换代码使用。Make a mark for replacing codes. */ $replaceMark = '//**//'; $modelLines .= "\n$replaceMark\n}"; /* 生成一个临时的model扩展文件,并加载,用于后续的hook文件加载使用。Create a tmp merged model file and import it for merge hook codes using. */ $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; if(@file_put_contents($tmpModelFile, $modelLines)) { if(!class_exists($tmpModelClass)) include $tmpModelFile; return $modelLines; } $this->triggerError("ERROR: $tmpModelFile not writable.", __FILE__, __LINE__, true); } /** * 合并model的hook脚本。Merge hook files for a model. * * @access public * @return void */ public function mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile) { /* 定义相关变量。Init vars. */ $modelClass = $moduleName . 'Model'; $extModelClass = 'ext' . $modelClass; $tmpModelClass = 'tmpExt' . $modelClass; $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; $replaceMark = '//**//'; /* 读取hook文件。Get hook codes need to merge. */ $hookCodes = array(); foreach($hookFiles as $hookFile) { /* 通过文件名获得其对应的方法名。Get methods according it's filename. */ $fileName = baseName($hookFile); list($method) = explode('.', $fileName); $hookCodes[$method][] = self::removePHPTAG($hookFile); } /* 合并Hook文件。Cycle the hook methods and merge hook codes. */ $hookedMethods = array_keys($hookCodes); $mainModelCodes = file($mainModelFile); $mergedModelCodes = file($tmpModelFile); foreach($hookedMethods as $method) { /* 通过反射获得hook脚本对应的方法所在的文件和起止行数。Reflection the hooked method to get it's defined position. */ if(!method_exists($tmpModelClass, $method)) continue; $methodRelfection = new reflectionMethod($tmpModelClass, $method); $definedFile = $methodRelfection->getFileName(); $startLine = $methodRelfection->getStartLine(); $endLine = $methodRelfection->getEndLine(); /* 将Hook脚本和老的代码合并在一起,并替换原来的定义。Merge hook codes with old codes and replace back. */ $oldCodes = $definedFile == $tmpModelFile ? $mergedModelCodes : $mainModelCodes; $oldCodes = join("", array_slice($oldCodes, $startLine - 1, $endLine - $startLine + 1)); $openBrace = strpos($oldCodes, '{'); $newCodes = substr($oldCodes, 0, $openBrace + 1) . "\n" . join("\n", $hookCodes[$method]) . substr($oldCodes, $openBrace + 1); if($definedFile == $tmpModelFile) $modelLines = str_replace($oldCodes, $newCodes, $modelLines); if($definedFile != $tmpModelFile) $modelLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $modelLines); } /* 保存最终的Model文件。Save the last merged model file. */ $modelLines = str_replace($tmpModelClass, $extModelClass, $modelLines); file_put_contents($mergedModelFile, $modelLines); unlink($tmpModelFile); } /** * Remove tags of PHP * * @param string $fileName * @static * @access public * @return string */ static public function removePHPTAG($fileName) { $code = trim(file_get_contents($fileName)); if(strpos($code, '') !== false) $code = rtrim($code, '?>'); return trim($code); } //-------------------- 路由相关方法(Routing related methods) --------------------// /** * 设置路由(PATH_INFO 方式): * 1.设置模块名; * 2.设置方法名; * 3.设置控制器文件。 * * Set the route according to PATH_INFO. * 1. set the module name. * 2. set the method name. * 3. set the control file. * * @access public * @return void */ public function setRouteByPathInfo() { if(!empty($this->URI)) { /* * 根据$requestFix分割符,分割网址。 * There's the request seperator, split the URI by it. **/ if(strpos($this->URI, $this->config->requestFix) !== false) { $items = explode($this->config->requestFix, $this->URI); $this->setModuleName($items[0]); $this->setMethodName($items[1]); } /* * 如果网址中没有分隔符,使用默认的方法。 * No reqeust seperator, use the default method name. **/ else { $this->setModuleName($this->URI); $this->setMethodName($this->config->default->method); } } else { $this->setModuleName($this->config->default->module); // 使用默认模块 use the default module. $this->setMethodName($this->config->default->method); // 使用默认方法 use the default method. } $this->setControlFile(); } /** * 设置路由(GET 方式): * 1.设置模块名; * 2.设置方法名; * 3.设置控制器文件。 * * Set the route according to GET. * 1. set the module name. * 2. set the method name. * 3. set the control file. * * @access public * @return void */ public function setRouteByGET() { $moduleName = isset($_GET[$this->config->moduleVar]) ? strtolower($_GET[$this->config->moduleVar]) : $this->config->default->module; $methodName = isset($_GET[$this->config->methodVar]) ? strtolower($_GET[$this->config->methodVar]) : $this->config->default->method; $this->setModuleName($moduleName); $this->setControlFile(); $this->setMethodName($methodName); } /** * 加载一个模块: * 1. 引入控制器文件或扩展的方法文件; * 2. 创建control对象; * 3. 解析url,得到请求的参数; * 4. 使用call_user_function_array调用相应的方法。 * * Load a module. * 1. include the control file or the extension action file. * 2. create the control object. * 3. set the params passed in through url. * 4. call the method by call_user_function_array * * @access public * @return bool|object if the module object of die. */ public function loadModule() { $appName = $this->appName; $moduleName = $this->moduleName; $methodName = $this->methodName; /* * 引入该模块的control文件。 * Include the control file of the module. **/ $file2Included = $this->setActionExtFile() ? $this->extActionFile : $this->controlFile; chdir(dirname($file2Included)); include $file2Included; /* * 设置control的类名。 * Set the class name of the control. **/ $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName; if(!class_exists($className)) $this->triggerError("the control $className not found", __FILE__, __LINE__, $exit = true); /* * 创建control类的实例。 * Create a instance of the control. **/ $module = new $className(); if(!method_exists($module, $methodName)) $this->triggerError("the module $moduleName has no $methodName method", __FILE__, __LINE__, $exit = true); $this->control = $module; /* include default value for module*/ $defaultValueFiles = glob($this->getTmpRoot() . "defaultvalue/*.php"); if($defaultValueFiles) foreach($defaultValueFiles as $file) include $file; /* * 使用反射机制获取函数参数的默认值。 * Get the default settings of the method to be called using the reflecting. * * */ $defaultParams = array(); $methodReflect = new reflectionMethod($className, $methodName); foreach($methodReflect->getParameters() as $param) { $name = $param->getName(); $default = '_NOT_SET'; if(isset($paramDefaultValue[$appName][$className][$methodName][$name])) { $default = $paramDefaultValue[$appName][$className][$methodName][$name]; } elseif(isset($paramDefaultValue[$className][$methodName][$name])) { $default = $paramDefaultValue[$className][$methodName][$name]; } elseif($param->isDefaultValueAvailable()) { $default = $param->getDefaultValue(); } $defaultParams[$name] = $default; } /** * 根据PATH_INFO或者GET方式设置请求的参数。 * Set params according PATH_INFO or GET. */ if($this->config->requestType != 'GET') { $this->setParamsByPathInfo($defaultParams); } else { $this->setParamsByGET($defaultParams); } if($this->config->framework->filterParam == 2) { $_GET = validater::filterParam($_GET, 'get'); $_COOKIE = validater::filterParam($_COOKIE, 'cookie'); } /* 调用该方法 Call the method. */ call_user_func_array(array($module, $methodName), $this->params); return $module; } /** * 设置请求的参数(PATH_INFO 方式)。 * Set the params by PATH_INFO. * * @param array $defaultParams the default settings of the params. * @param string $type * @access public * @return void */ public function setParamsByPathInfo($defaultParams = array(), $type = '') { $params = array(); if($type != 'fetch') { /* 分割URI。 Spit the URI. */ $items = explode($this->config->requestFix, $this->URI); $itemCount = count($items); /** * 前两项为模块名和方法名,参数从下标2开始。 * The first two item is moduleName and methodName. So the params should begin at 2. **/ for($i = 2; $i < $itemCount; $i ++) { $key = key($defaultParams); // Get key from the $defaultParams. if(empty($key)) continue; $params[$key] = $items[$i]; next($defaultParams); } } $this->params = $this->mergeParams($defaultParams, $params); } /** * 设置请求的参数(GET 方式)。 * Set the params by GET. * * @param array $defaultParams the default settings of the params. * @param string $type * @access public * @return void */ public function setParamsByGET($defaultParams, $type = '') { $params = array(); if($type != 'fetch') { /* Unset moduleVar, methodVar, viewVar and session 变量, 剩下的作为参数。 */ /* Unset the moduleVar, methodVar, viewVar and session var, all the left are the params. */ unset($_GET[$this->config->moduleVar]); unset($_GET[$this->config->methodVar]); unset($_GET[$this->config->viewVar]); unset($_GET[$this->config->sessionVar]); $params = $_GET; } $this->params = $this->mergeParams($defaultParams, $params); } /** * 合并请求的参数和默认参数,这样就可以省略已经有默认值的参数了。 * Merge the params passed in and the default params. Thus the params which have default values needn't pass value, just like a function. * * @param array $defaultParams the default params defined by the method. * @param array $passedParams the params passed in through url. * @access public * @return array the merged params. */ public function mergeParams($defaultParams, $passedParams) { global $filter; /* Remove these two params. */ unset($passedParams['onlybody']); unset($passedParams['HTTP_X_REQUESTED_WITH']); /* Check params from URL. */ $nameRule = isset($filter->{$this->moduleName}->{$this->methodName}->paramName) ? $filter->{$this->moduleName}->{$this->methodName}->paramName : $filter->default->paramName; foreach($passedParams as $param => $value) { if(!validater::checkByRule($param, $nameRule)) die('Bad Request!'); $valueRule = $filter->default->paramValue; if(isset($filter->{$this->moduleName}->{$this->methodName}->paramValue[$param])) { $valueRule = $filter->{$this->moduleName}->{$this->methodName}->paramValue[$param]; } if($value and !validater::checkByRule($value, $valueRule)) die('Bad Request!'); } $passedParams = array_values($passedParams); $i = 0; foreach($defaultParams as $key => $defaultValue) { if(isset($passedParams[$i])) { $defaultParams[$key] = strip_tags($passedParams[$i]); } else { if($defaultValue === '_NOT_SET') $this->triggerError("The param '$key' should pass value. ", __FILE__, __LINE__, $exit = true); } $i ++; } return $defaultParams; } /** * 获取$moduleName变量。 * Get the $moduleName var. * * @access public * @return string */ public function getModuleName() { return $this->moduleName; } /** * 获取$controlFile变量。 * Get the $controlFile var. * * @access public * @return string */ public function getControlFile() { return $this->controlFile; } /** * 获取$methodName变量。 * Get the $methodName var. * * @access public * @return string */ public function getMethodName() { return $this->methodName; } /** * 获取$param变量。 * Get the $param var. * * @access public * @return string */ public function getParams() { return $this->params; } //-------------------- 常用的工具方法(Tool methods) ------------------// /** * 从类库中加载一个类文件。 * * Load a class file. * * @param string $className the class name * @param bool $static statis class or not * @access public * @return object|bool the instance of the class or just true. */ public function loadClass($className, $static = false) { $className = strtolower($className); /* 搜索$coreLibRoot(Search in $coreLibRoot) */ $classFile = $this->coreLibRoot . $className; if(is_dir($classFile)) $classFile .= DS . $className; $classFile .= '.class.php'; if(!helper::import($classFile)) $this->triggerError("class file $classFile not found", __FILE__, __LINE__, $exit = true); /* 如果是静态调用,则返回(If staitc, return) */ if($static) return true; /* 实例化该类(Instance it) */ global $$className; if(!class_exists($className)) $this->triggerError("the class $className not found in $classFile", __FILE__, __LINE__, $exit = true); if(!is_object($$className)) $$className = new $className(); return $$className; } /** * 加载整个应用公共的配置文件。 * Load the common config files for the app. * * @access public * @return void */ public function loadMainConfig() { /* 初始化$config对象。Init the $config object. */ global $config, $filter; if(!is_object($config)) $config = new config(); $this->config = $config; /* 加载主配置文件。 Load the main config file. */ $mainConfigFile = $this->configRoot . 'config.php'; if(!file_exists($mainConfigFile)) $this->triggerError("The main config file $mainConfigFile not found", __FILE__, __LINE__, $exit = true); include $mainConfigFile; } /** * 当multiSite功能打开的时候,加载额外的配置文件。 * When multiSite feature enabled, load extra config file. * * @access public * @return void */ public function loadExtraConfig() { global $config; $multiConfigFile = $this->configRoot . 'multi.php'; if(file_exists($multiConfigFile)) include $multiConfigFile; $siteConfigFile = $this->configRoot . "sites/{$this->siteCode}.php"; if(file_exists($siteConfigFile)) include $siteConfigFile; } /** * 加载模块的config文件,返回全局$config对象。 * 如果该模块是common,加载$configRoot的配置文件,其他模块则加载其模块的配置文件。 * * Load config and return it as the global config object. * If the module is common, search in $configRoot, else in $modulePath. * * @param string $moduleName module name * @param string $appName app name * @param bool $exitIfNone exit or not * @access public * @return object|bool the config object or false. */ public function loadModuleConfig($moduleName, $appName = '') { global $config; if($config and (!isset($config->$moduleName) or !is_object($config->$moduleName))) $config->$moduleName = new stdclass(); /* 初始化数组。Init the variables. */ $extConfigFiles = array(); $commonExtConfigFiles = array(); $siteExtConfigFiles = array(); /* 先获得模块的主配置文件。Get the main config file for current module first. */ $mainConfigFile = $this->getModulePath($appName, $moduleName) . 'config.php'; /* 查找扩展配置文件。Get extension config files. */ if($config->framework->extensionLevel > 0) $extConfigPath = $this->getModuleExtPath($appName, $moduleName, 'config'); if($config->framework->extensionLevel >= 1 and !empty($extConfigPath['common'])) $commonExtConfigFiles = helper::ls($extConfigPath['common'], '.php'); if($config->framework->extensionLevel == 2 and !empty($extConfigPath['site'])) $siteExtConfigFiles = helper::ls($extConfigPath['site'], '.php'); $extConfigFiles = array_merge($commonExtConfigFiles, $siteExtConfigFiles); /* 将主配置文件和扩展配置文件合并在一起。Put the main config file and extension config files together. */ $configFiles = array_merge(array($mainConfigFile), $extConfigFiles); /* 加载每一个配置文件。Load every config file. */ static $loadedConfigs = array(); foreach($configFiles as $configFile) { if(in_array($configFile, $loadedConfigs)) continue; if(file_exists($configFile)) include $configFile; $loadedConfigs[] = $configFile; } /* 加载数据库中与本模块相关的配置项。Merge from the db configs. */ if($moduleName != 'common') { if(isset($config->system->$moduleName)) $this->mergeConfig($config->system->$moduleName, $moduleName); if(isset($config->personal->$moduleName)) $this->mergeConfig($config->personal->$moduleName, $moduleName); } } /** * Merge db config. * * @param array $dbConfig * @param string $moduleName * @access public * @return void */ public function mergeConfig($dbConfig, $moduleName = 'common') { global $config; /* 如果没有设置本模块配置,则首先进行初始化。Init the $config->$moduleName if not set.*/ if($moduleName != 'common' and !isset($config->$moduleName)) $config->$moduleName = new stdclass(); $config2Merge = $config; if($moduleName != 'common') $config2Merge = $config->$moduleName; foreach($dbConfig as $item) { if($item->section) { if(!isset($config2Merge->{$item->section})) $config2Merge->{$item->section} = new stdclass(); if(is_object($config2Merge->{$item->section})) { $config2Merge->{$item->section}->{$item->key} = $item->value; } } else { $config2Merge->{$item->key} = $item->value; } } } /** * 向客户端输出配置参数,客户端可以根据这些参数实现和调整请求的逻辑。 * Export the config params to the client, thus the client can adjust it's logic according the config. * * @access public * @return void */ public function exportConfig() { $view = new stdclass(); $view->version = $this->config->version; $view->requestType = $this->config->requestType; $view->requestFix = $this->config->requestFix; $view->moduleVar = $this->config->moduleVar; $view->methodVar = $this->config->methodVar; $view->viewVar = $this->config->viewVar; $view->sessionVar = $this->config->sessionVar; $this->session->set('random', mt_rand(0, 10000)); $view->sessionName = session_name(); $view->sessionID = session_id(); $view->random = $this->session->random; $view->expiredTime = ini_get('session.gc_maxlifetime'); $view->serverTime = time(); echo json_encode($view); } /** * 加载语言文件,返回全局$lang对象。 * Load lang and return it as the global lang object. * * @param string $moduleName the module name * @param string $appName the app name * @access public * @return bool|object the lang object or false. */ public function loadLang($moduleName, $appName = '') { /* 初始化变量。Init vars. */ $modulePath = $this->getModulePath($appName, $moduleName); $extLangFiles = array(); $langFilesToLoad = array(); /* 判断主语言文件是否存在。Whether the main lang file exists or not. */ $mainLangFile = $modulePath . 'lang' . DS . $this->clientLang . '.php'; if(file_exists($mainLangFile)) $langFilesToLoad[] = $mainLangFile; /* 获取扩展语言文件。If extensionLevel > 0, get extension lang files. */ if($this->config->framework->extensionLevel > 0) { $commonExtLangFiles = array(); $siteExtLangFiles = array(); $extLangPath = $this->getModuleExtPath($appName, $moduleName, 'lang'); if($this->config->framework->extensionLevel >= 1 and !empty($extLangPath['common'])) $commonExtLangFiles = helper::ls($extLangPath['common'] . $this->clientLang, '.php'); if($this->config->framework->extensionLevel == 2 and !empty($extLangPath['site'])) $siteExtLangFiles = helper::ls($extLangPath['site'] . $this->clientLang, '.php'); $extLangFiles = array_merge($commonExtLangFiles, $siteExtLangFiles); } /* 计算最终要加载的语言文件。 Get the lang files to be loaded. */ $langFilesToLoad = array_merge($langFilesToLoad, $extLangFiles); if(empty($langFilesToLoad)) return false; /* 加载语言文件。Load lang files. */ global $lang; if(!is_object($lang)) $lang = new language(); if(!isset($lang->$moduleName)) $lang->$moduleName = new stdclass(); static $loadedLangs = array(); foreach($langFilesToLoad as $langFile) { if(in_array($langFile, $loadedLangs)) continue; include $langFile; $loadedLangs[] = $langFile; } $this->lang = $lang; return $lang; } /** * 连接数据库。 * Connect to database. * * @access public * @return void */ public function connectDB() { global $config, $dbh, $slaveDBH; if(!isset($config->installed) or !$config->installed) return; if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db); if(isset($config->slaveDB->host)) $this->slaveDBH = $slaveDBH = $this->connectByPDO($config->slaveDB); } /** * 使用PDO连接数据库。 * Connect database by PDO. * * @param object $params the database params. * @access public * @return object|bool */ public function connectByPDO($params) { if(!isset($params->driver)) self::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, $exit = true); if(!isset($params->user)) return false; if($params->driver == 'mysql') { $dsn = "mysql:host={$params->host}; port={$params->port}; dbname={$params->name}"; } try { $dbh = new PDO($dsn, $params->user, $params->password, array(PDO::ATTR_PERSISTENT => $params->persistant)); $dbh->exec("SET NAMES {$params->encoding}"); /* * 如果系统是Linux,开启仿真预处理和缓冲查询。 * If run on linux, set emulatePrepare and bufferQuery to true. **/ if(!isset($params->emulatePrepare) and PHP_OS == 'Linux') $params->emulatePrepare = true; if(!isset($params->bufferQuery) and PHP_OS == 'Linux') $params->bufferQuery = true; $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); if(isset($params->strictMode) and $params->strictMode == false) $dbh->exec("SET @@sql_mode= ''"); if(isset($params->emulatePrepare)) $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $params->emulatePrepare); if(isset($params->bufferQuery)) $dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $params->bufferQuery); return $dbh; } catch (PDOException $exception) { self::triggerError($exception->getMessage(), __FILE__, __LINE__, $exit = true); } } //-------------------- 错误处理方法(Error methods) ------------------// /** * 程序停止时执行的函数。 * The shutdown handler. * * @access public * @return void */ public function shutdown() { /* 如果debug模式开启,保存sql语句(If debug on, save sql queries) */ if(!empty($this->config->debug)) $this->saveSQL(); /* * 发现错误,保存到日志中。 * If any error occers, save it. * */ if(!function_exists('error_get_last')) return; $error = error_get_last(); if($error) $this->saveError($error['type'], $error['message'], $error['file'], $error['line']); } /** * 触发一个错误。 * Trigger an error. * * @param string $message 错误信息 error message * @param string $file 所在文件 the file error occers * @param int $line 错误行 the line error occers * @param bool $exit 是否停止程序 exit the program or not * @access public * @return void */ public function triggerError($message, $file, $line, $exit = false) { /* 设置错误信息(Set the error info) */ $message = htmlspecialchars($message); $log = "ERROR: $message in $file on line $line"; if(isset($_SERVER['SCRIPT_URI'])) $log .= ", request: $_SERVER[SCRIPT_URI]";; $trace = debug_backtrace(); extract($trace[0]); extract($trace[1]); $log .= ", last called by $file on line $line through function $function.\n"; /* 触发错误(Trigger the error) */ trigger_error($log, $exit ? E_USER_ERROR : E_USER_WARNING); } /** * 保存错误信息。 * Save error info. * * @param int $level * @param string $message * @param string $file * @param int $line * @access public * @return void */ public function saveError($level, $message, $file, $line) { if(empty($this->config->debug)) return true; if(!is_dir($this->logRoot)) return true; if(!is_writable($this->logRoot)) return true; /* * 删除设定时间之前的日志。 * Delete the log before the set time. **/ if(mt_rand(0, 10) == 1) { $logDays = isset($this->config->framework->logDays) ? $this->config->framework->logDays : 14; $dayTime = time() - $logDays * 24 * 3600; foreach(glob($this->getLogRoot() . '*') as $logFile) { if(filemtime($logFile) <= $dayTime) unlink($logFile); } } /* * 忽略该错误:Redefining already defined constructor。 * Skip the error: Redefining already defined constructor. **/ if(strpos($message, 'Redefining') !== false) return true; /* * 设置错误信息。 * Set the error info. **/ $errorLog = "\n" . date('H:i:s') . " $message in $file on line $line "; $errorLog .= "when visiting " . htmlspecialchars($this->getURI()) . "\n"; /* * 为了安全起见,对公网环境隐藏脚本路径。 * If the ip is pulic, hidden the full path of scripts. */ if(!defined('IN_SHELL') and !($this->server->remote_addr == '127.0.0.1' or filter_var($this->server->remote_addr, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)) { $errorLog = str_replace($this->getBasePath(), '', $errorLog); } /* 保存到日志文件(Save to log file) */ $errorFile = $this->logRoot . 'php.' . date('Ymd') . '.log.php'; if(!is_file($errorFile)) file_put_contents($errorFile, "\n"); $fh = fopen($errorFile, 'a'); if($fh) fwrite($fh, strip_tags($errorLog)) and fclose($fh); /* * 如果debug > 1,显示warning, notice级别的错误。 * If the debug > 1, show warning, notice error. **/ if($level == E_NOTICE or $level == E_WARNING or $level == E_STRICT or $level == 8192) // 8192: E_DEPRECATED { if(!empty($this->config->debug) and $this->config->debug > 1) { $cmd = "vim +$line $file"; $size = strlen($cmd); echo "
$message: ";
                echo "
"; } } /* * 如果是严重错误,停止程序。 * If error level is serious, die. * */ if($level == E_ERROR or $level == E_PARSE or $level == E_CORE_ERROR or $level == E_COMPILE_ERROR or $level == E_USER_ERROR) { if(empty($this->config->debug)) die(); if(PHP_SAPI == 'cli') die($errorLog); $htmlError = ""; $htmlError .= "" . nl2br($errorLog) . ""; die($htmlError); } } /** * 保存sql语句。 * Save the sql. * * @access public * @return void */ public function saveSQL() { if(!$this->config->debug) return true; if(!class_exists('dao')) return; $sqlLog = $this->getLogRoot() . 'sql.' . date('Ymd') . '.log.php'; if(!is_file($sqlLog)) file_put_contents($sqlLog, "\n"); $fh = @fopen($sqlLog, 'a'); if(!$fh) return false; fwrite($fh, date('Ymd H:i:s') . ": " . $this->getURI() . "\n"); foreach(dao::$querys as $query) fwrite($fh, " $query\n"); fwrite($fh, "\n"); fclose($fh); } } /** * config类。 * The config class. * * @package framework */ class config { /** * 设置成员变量,成员可以是'db.user'类似的格式。 * Set the value of a member. the member can be the format like db.user. * * * set('db.user', 'wwccss'); * ?> * * @param string $key the key of the member * @param mixed $value the value * @access public * @return void */ public function set($key, $value) { helper::setMember('config', $key, $value); } } /** * lang类。 * The lang class. * * @package framework */ class language { /** * 设置成员变量,成员可以是'db.user'类似的格式。 * Set the value of a member. the member can be the foramt like db.user. * * * set('version', '1.0); * ?> * * @param string $key 成员的键名,可以是father.child的形式。 * the key of the member, can be father.child * @param mixed $value the value * @access public * @return void */ public function set($key, $value) { helper::setMember('lang', $key, $value); } /** * 显示一个成员的值。 * Show a member. * * @param object $obj the object * @param string $key the key * @access public * @return void */ public function show($obj, $key) { $obj = (array)$obj; echo isset($obj[$key]) ? $obj[$key] : ''; } } /** * 超级对象类,转化超级全局变量。 * The super object class. * * @package framework */ class super { /** * 构造函数,设置超级变量名。 * Construct, set the var scope. * * @param string $scope the scope, can be server, post, get, cookie, session, global * @access public * @return void */ public function __construct($scope) { $this->scope = $scope; } /** * 设置超级变量的成员值。 * Set one member value. * * @param string the key * @param mixed $value the value * @access public * @return void */ public function set($key, $value) { if($this->scope == 'post') { $_POST[$key] = $value; } elseif($this->scope == 'get') { $_GET[$key] = $value; } elseif($this->scope == 'server') { $_SERVER[$key] = $value; } elseif($this->scope == 'cookie') { $_COOKIE[$key] = $value; } elseif($this->scope == 'session') { $_SESSION[$key] = $value; } elseif($this->scope == 'env') { $_ENV[$key] = $value; } elseif($this->scope == 'global') { $GLOBALS[$key] = $value; } } /** * 超级变量的魔术方法,比如用$post->key访问$_POST['key']。 * The magic get method. * * @param string $key the key * @access public * @return mixed|bool return the value of the key or false. */ public function __get($key) { if($this->scope == 'post') { if(isset($_POST[$key])) return $_POST[$key]; return false; } elseif($this->scope == 'get') { if(isset($_GET[$key])) return $_GET[$key]; return false; } elseif($this->scope == 'server') { if($key == 'ajax') return isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? true : false; if(isset($_SERVER[$key])) return $_SERVER[$key]; $key = strtoupper($key); if(isset($_SERVER[$key])) return $_SERVER[$key]; return false; } elseif($this->scope == 'cookie') { if(isset($_COOKIE[$key])) return $_COOKIE[$key]; return false; } elseif($this->scope == 'session') { if(isset($_SESSION[$key])) return $_SESSION[$key]; return false; } elseif($this->scope == 'env') { if(isset($_ENV[$key])) return $_ENV[$key]; return false; } elseif($this->scope == 'global') { if(isset($GLOBALS[$key])) return $GLOBALS[$key]; return false; } else { return false; } } /** * 打印变量的详细结构。 * Print the structure. * * @access public * @return void */ public function a() { if($this->scope == 'post') a($_POST); if($this->scope == 'get') a($_GET); if($this->scope == 'server') a($_SERVER); if($this->scope == 'cookie') a($_COOKIE); if($this->scope == 'session') a($_SESSION); if($this->scope == 'env') a($_ENV); if($this->scope == 'global') a($GLOBALS); } } ================================================ FILE: framework/control.class.php ================================================ 1 [a2] => 2 [a3] => stdClass Object ( [b1] => 3 [b2] => 4 ) [a4] => stdClass Object ( [b3] => stdClass Object ( [c1] => 5 ) ) [a5] => 6 [a6] => stdClass Object ( [b4] => 7 ) [a7] => stdClass Object ( [b5] => stdClass Object ( [c2] => 8 ) ) ) 3 10 ================================================ FILE: framework/tests/helper/.case002.expect ================================================ chunsheng wang'chun"sheng Array ( [0] => a [1] => b [2] => c ) stdClass Object ( ) localhost chunsheng'.wang Array ( [0] => 1 [1] => 2 [2] => 3 ) ================================================ FILE: framework/tests/helper/.case003.expect ================================================ /index/index.html /user/login.html /user/view/k1/v1/k2/v2.html /user/view/k1/v1/k2/v2.html /index/index.html /user/login.html /user/view/v1/v2.html /user/view/v1/v2.html /index-index.html /user-login.html /user-view-v1-v2.html /user-view-v1-v2.html /?m=index&f=index&t=html /?m=user&f=login&t=html /?m=user&f=view&t=html&k1=v1&k2=v2 /?m=user&f=view&t=html&k1=v1&k2=v2 ================================================ FILE: framework/tests/helper/.case004.expect ================================================ case004.php helper.class.php import1.php case004.php helper.class.php import1.php case004.php helper.class.php import1.php import2.php bool(false) ================================================ FILE: framework/tests/helper/case001.php ================================================ #!/usr/bin/env php * @package Testing * @version $Id$ * @link http://www.zentao.net * @license http://opensource.org/licenses/lgpl-3.0.html LGPL */ include '../../helper.class.php'; $array['a1'] = '1'; $array['a2'] = '2'; $array['a3']['b1'] = '3'; $array['a3']['b2'] = '4'; $array['a4']['b3']['c1'] = '5'; $array['a5'] = '6'; $array['a6']['b4'] = '7'; $array['a7']['b5']['c2'] = '8'; $config = new stdClass(); eval (helper::array2object($array, 'config')); print_r($config); echo $config->a3->b1; echo "\n"; helper::setMember('config', 'a3.b1', 10); echo $config->a3->b1; echo "\n"; ?> ================================================ FILE: framework/tests/helper/case002.php ================================================ #!/usr/bin/env php * @package Testing * @version $Id$ * @link http://www.zentao.net * @license http://opensource.org/licenses/lgpl-3.0.html LGPL */ include '../../helper.class.php'; $config = new stdClass(); /* 测试一维属性的修改。*/ $config->user = 'wwccss'; helper::setMember('config', 'user', 'chunsheng'); echo $config->user . "\n"; /* 赋值的变量含有单双引号。*/ $config->name = 'wwccss'; helper::setMember('config', 'name', "wang'chun\"sheng"); echo $config->name . "\n"; /* 赋值的变量为一个数组。*/ $config->users = array(1,2,3); helper::setMember('config', 'users', array('a', 'b', 'c')); print_r($config->users); /* 赋值的变量为一个对象。*/ $config->obj = array(1,2,3); helper::setMember('config', 'obj', new stdClass()); print_r($config->obj); /* 测试二维属性的修改。*/ $config->db->host = 'localhost'; $config->db->user = 'wwccss'; $config->db->param = array(); helper::setMember('config', 'db.host', "localhost"); helper::setMember('config', 'db.user', "chunsheng'.wang"); helper::setMember('config', 'db.param', array('1', '2', '3')); echo $config->db->host . "\n"; echo $config->db->user . "\n"; print_r($config->db->param); ?> ================================================ FILE: framework/tests/helper/case003.php ================================================ #!/usr/bin/env php * @package Testing * @version $Id$ * @link http://www.zentao.net * @license http://opensource.org/licenses/lgpl-3.0.html LGPL */ include '../../helper.class.php'; /* 实例化app的mock对象。*/ $app = new mockapp(); $app->setViewType('html'); /* 设置cfg配置,并将其转换为$config对象。*/ $cfg['webRoot'] = '/'; $cfg['requestType'] = 'PATH_INFO'; $cfg['requestFix'] = '/'; $cfg['pathType'] = 'full'; $cfg['moduleVar'] = 'm'; $cfg['methodVar'] = 'f'; $cfg['viewVar'] = 't'; eval(helper::array2Object($cfg, 'config')); /* PATH_INFO + FULL*/ $vars = array('k1' => 'v1', 'k2' => 'v2'); echo helper::createLink('index') . "\n"; // 只有模块名。 echo helper::createLink('user', 'login') . "\n"; // 增加方法名。 echo helper::createLink('user', 'view', $vars) . "\n"; // 增加参数。 $vars = 'k1=v1&k2=v2'; echo helper::createLink('user', 'view', $vars) . "\n\n"; // 参数改成str形式。 /* PATH_INFO + CLEAN */ $config->pathType = 'clean'; $vars = array('k1' => 'v1', 'k2' => 'v2'); echo helper::createLink('index') . "\n"; // 只有模块名。 echo helper::createLink('user', 'login') . "\n"; // 增加方法名。 echo helper::createLink('user', 'view', $vars) . "\n"; // 增加参数。 $vars = 'k1=v1&k2=v2'; echo helper::createLink('user', 'view', $vars) . "\n\n"; // 参数改成str形式。 /* PATH_INFO + CLEAN + REQUESTFIX */ $config->requestFix = '-'; $vars = array('k1' => 'v1', 'k2' => 'v2'); echo helper::createLink('index') . "\n"; // 只有模块名。 echo helper::createLink('user', 'login') . "\n"; // 增加方法名。 echo helper::createLink('user', 'view', $vars) . "\n"; // 增加参数。 $vars = 'k1=v1&k2=v2'; echo helper::createLink('user', 'view', $vars) . "\n\n"; // 参数改成str形式。 /* GET + CLEAN */ $config->requestType = 'GET'; $vars = array('k1' => 'v1', 'k2' => 'v2'); echo helper::createLink('index') . "\n"; // 只有模块名。 echo helper::createLink('user', 'login') . "\n"; // 增加方法名。 echo helper::createLink('user', 'view', $vars) . "\n"; // 增加参数。 $vars = 'k1=v1&k2=v2'; echo helper::createLink('user', 'view', $vars) . "\n"; // 参数改成str形式。 /** * app的mock对象。 * * @package Testing */ class mockapp { private $viewType; public function setViewType($viewType) { $this->viewType = $viewType; } public function getViewType() { return $this->viewType; } } ?> ================================================ FILE: framework/tests/helper/case004.php ================================================ #!/usr/bin/env php * @package Testing * @version $Id$ * @link http://www.zentao.net * @license http://opensource.org/licenses/lgpl-3.0.html LGPL */ include '../../helper.class.php'; /* 首次包含。*/ helper::import('import1.php'); printIncluded(); /* 重复包含。*/ helper::import('import1.php'); printIncluded(); /* 包含第二个文件。*/ helper::import('import2.php'); printIncluded(); /* 包含不存在的文件。*/ var_dump(helper::import('noexits.php')); /** * 只打印包含文件的文件名。 * * @access public * @return void */ function printIncluded() { $files = get_included_files(); foreach($files as $file) { echo basename($file) . "\n"; } echo "\n"; } ?> ================================================ FILE: framework/tests/helper/import1.php ================================================ * @package Testing * @version $Id$ * @link http://www.zentao.net * @license http://opensource.org/licenses/lgpl-3.0.html LGPL */ class myclass1{}; ?> ================================================ FILE: framework/tests/helper/import2.php ================================================ * @package Testing * @version $Id$ * @link http://www.zentao.net * @license http://opensource.org/licenses/lgpl-3.0.html LGPL */ class myclass2{}; ?> ================================================ FILE: index.php ================================================ loadCommon(); // 加载common模块。Load the common module. $app->parseRequest(); // 解析请求。 Parse the request. $app->loadModule(); // 加载模块。 Load module. echo helper::removeUTF8Bom(ob_get_clean()); // 输出内容。 Print the output. ================================================ FILE: js/my.js ================================================ /** * The js file of ZenTaoPHP. * * The author disclaims copyright to this source code. In place of * a legal notice, here is a blessing: * * May you do good and not evil. * May you find forgiveness for yourself and forgive others. * May you share freely, never taking more than you give. */ /** * Create link. * * @param string $moduleName * @param string $methodName * @param string $vars * @param string $viewType * @access public * @return string */ function createLink(moduleName, methodName, vars, viewType) { if(!viewType) viewType = config.defaultView; if(vars) { vars = vars.split('&'); for(i = 0; i < vars.length; i ++) vars[i] = vars[i].split('='); } if(config.requestType != 'GET') { if(config.requestType == 'PATH_INFO') link = config.webRoot + moduleName + config.requestFix + methodName; if(config.requestType == 'PATH_INFO2') link = config.webRoot + 'index.php/' + moduleName + config.requestFix + methodName; if(vars) { for(i = 0; i < vars.length; i ++) link += config.requestFix + vars[i][1]; } link += '.' + viewType; } else { link = config.router + '?' + config.moduleVar + '=' + moduleName + '&' + config.methodVar + '=' + methodName + '&' + config.viewVar + '=' + viewType; if(vars) for(i = 0; i < vars.length; i ++) link += '&' + vars[i][0] + '=' + vars[i][1]; } return link; } /** * Switch language. * * @param object $switcher * @access public * @return void */ function switchLang(switcher) { $.cookie('lang', $(switcher).data('lang')); location.href = location.href; } ================================================ FILE: lib/base/dao/dao.class.php ================================================ table的别名。 * The alias of $this->table. * * @var string * @access public */ public $alias; /** * 查询的字段。 * The fields will be returned. * * @var string * @access public */ public $fields; /** * 查询模式,raw模式用于正常的select update等sql拼接操作,magic模式用于findByXXX等魔术方法。 * The query mode, raw or magic. * * This var is used to diff dao::from() with sql::from(). * * @var string * @access public */ public $mode; /** * 执行方式:insert, select, update, delete, replace。 * The query method: insert, select, update, delete, replace. * * @var string * @access public */ public $method; /** * 是否自动增加lang条件。 * If auto add lang statement. * * @var bool * @access public */ public $autoLang; /** * 需要修复表的错误代码 * The sql code of need repair table. * * @var string * @access public */ public $repairCode = '|1034|1035|1194|1195|1459|'; /** * 执行的请求,所有的查询都保存在该数组。 * The queries executed. Every query will be saved in this array. * * @var array * @access public */ static public $querys = array(); /** * 存放错误的数组。 * The errors. * * @var array * @access public */ static public $errors = array(); /** * 缓存。 * The cache. * * @var array * @access public */ static public $cache = array(); /** * 构造方法。 * The construct method. * * @access public * @return void */ public function __construct() { global $app, $config, $lang, $dbh, $slaveDBH; $this->app = $app; $this->config = $config; $this->lang = $lang; $this->dbh = $dbh; $this->slaveDBH = $slaveDBH ? $slaveDBH : false; $this->reset(); } /** * 设置$table属性。 * Set the $table property. * * @param string $table * @access public * @return void */ public function setTable($table) { $this->table = $table; } /** * 设置$alias属性。 * Set the $alias property. * * @param string $alias * @access public * @return void */ public function setAlias($alias) { $this->alias = $alias; } /** * 设置$fields属性。 * Set the $fields property. * * @param string $fields * @access public * @return void */ public function setFields($fields) { $this->fields = $fields; } /** * 设置autoLang项。 * Set autoLang item. * * @param bool $autoLang * @access public * @return void */ public function setAutoLang($autoLang) { $this->autoLang = $autoLang; return $this; } /** * 重置属性。 * Reset the vars. * * @access public * @return void */ public function reset() { $this->setFields(''); $this->setTable(''); $this->setAlias(''); $this->setMode(''); $this->setMethod(''); $this->setAutoLang(isset($this->config->framework->autoLang) and $this->config->framework->autoLang); } //-----根据请求的方式,调用sql类相应的方法(Call according method of sql class by query method. -----// /** * 设置请求模式。像findByxxx之类的方法,使用的是magic模式;其他方法使用的是raw模式。 * Set the query mode. If the method if like findByxxx, the mode is magic. Else, the mode is raw. * * @param string $mode magic|raw * @access public * @return void */ public function setMode($mode = '') { $this->mode = $mode; } /** * 设置请求方法:select|update|insert|delete|replace 。 * Set the query method: select|update|insert|delete|replace * * @param string $method * @access public * @return void */ public function setMethod($method = '') { $this->method = $method; } /** * 开始事务。 * Begin Transaction * * @access public * @return void */ public function begin() { $this->dbh->beginTransaction(); } /** * 事务回滚。 * Roll back * * @access public * @return void */ public function rollBack() { $this->dbh->rollBack(); } /** * 提交事务。 * Commits a transaction. * * @access public * @return void */ public function commit() { $this->dbh->commit(); } /** * select方法,调用sql::select()。 * The select method, call sql::select(). * * @param string $fields * @access public * @return object the dao object self. */ public function select($fields = '*') { $this->setMode('raw'); $this->setMethod('select'); $this->sqlobj = sql::select($fields); return $this; } /** * 获取查询记录条数。 * The count method, call sql::select() and from(). * use as $this->dao->select()->from(TABLE_BUG)->where()->count(); * * @param string $distinctField * @access public * @return void */ public function count($distinctField = '') { /* 获得SELECT,FROM的位置,使用count(*)替换其字段。 */ /* Get the SELECT, FROM position, thus get the fields, replace it by count(*). */ $sql = $this->get(); $selectPOS = strpos($sql, 'SELECT') + strlen('SELECT'); $fromPOS = strpos($sql, 'FROM'); $fields = substr($sql, $selectPOS, $fromPOS - $selectPOS ); $countField = $distinctField ? 'distinct ' . $distinctField : '*'; $sql = str_replace($fields, " COUNT($countField) AS recTotal ", substr($sql, 0, $fromPOS)) . substr($sql, $fromPOS); /* * 去掉SQL语句中order和limit之后的部分。 * Remove the part after order and limit. **/ $subLength = strlen($sql); $orderPOS = strripos($sql, 'order by'); $limitPOS = strripos($sql, 'limit'); if($limitPOS) $subLength = $limitPOS; if($orderPOS) $subLength = $orderPOS; $sql = substr($sql, 0, $subLength); self::$querys[] = $sql; /* * 获取记录数。 * Get the records count. **/ try { $row = $this->dbh->query($sql)->fetch(PDO::FETCH_OBJ); } catch (PDOException $e) { $this->sqlError($e); } return is_object($row) ? $row->recTotal : 0; } /** * update方法,调用sql::update()。 * The update method, call sql::update(). * * @param string $table * @access public * @return object the dao object self. */ public function update($table) { $this->setMode('raw'); $this->setMethod('update'); $this->sqlobj = sql::update($table); $this->setTable($table); return $this; } /** * delete方法,调用sql::delete()。 * The delete method, call sql::delete(). * * @access public * @return object the dao object self. */ public function delete() { $this->setMode('raw'); $this->setMethod('delete'); $this->sqlobj = sql::delete(); return $this; } /** * insert方法,调用sql::insert()。 * The insert method, call sql::insert(). * * @param string $table * @access public * @return object the dao object self. */ public function insert($table) { $this->setMode('raw'); $this->setMethod('insert'); $this->sqlobj = sql::insert($table); $this->setTable($table); return $this; } /** * replace方法,调用sql::replace()。 * The replace method, call sql::replace(). * * @param string $table * @access public * @return object the dao object self. */ public function replace($table) { $this->setMode('raw'); $this->setMethod('replace'); $this->sqlobj = sql::replace($table); $this->setTable($table); return $this; } /** * 设置要操作的表。 * Set the from table. * * @param string $table * @access public * @return object the dao object self. */ public function from($table) { $this->setTable($table); if($this->mode == 'raw') $this->sqlobj->from($table); return $this; } /** * 设置字段。 * Set the fields. * * @param string $fields * @access public * @return object the dao object self. */ public function fields($fields) { $this->setFields($fields); return $this; } /** * 表别名,相当于sql里的AS。(as是php的关键词,使用alias代替) * Alias a table, equal the AS keyword. (Don't use AS, because it's a php keyword.) * * @param string $alias * @access public * @return object the dao object self. */ public function alias($alias) { if(empty($this->alias)) $this->setAlias($alias); $this->sqlobj->alias($alias); return $this; } /** * 设置需要更新或插入的数据。 * Set the data to update or insert. * * @param object $data the data object or array * @access public * @return object the dao object self. */ public function data($data, $skipFields = '') { if(!is_object($data)) $data = (object)$data; if($this->autoLang and !isset($data->lang)) { $data->lang = $this->app->getClientLang(); if(isset($this->app->config->cn2tw) and $this->app->config->cn2tw and $data->lang == 'zh-tw') $data->lang = 'zh-cn'; if(defined('RUN_MODE') and RUN_MODE == 'front' and !empty($this->app->config->cn2tw)) $data->lang = str_replace('zh-tw', 'zh-cn', $data->lang); } $this->sqlobj->data($data, $skipFields); return $this; } //-------------------- sql相关的方法(The sql related method) --------------------// /** * 获取sql字符串。 * Get the sql string. * * @access public * @return string the sql string after process. */ public function get() { return $this->processKeywords($this->processSQL()); } /** * 打印sql字符串。 * Print the sql string. * * @access public * @return void */ public function printSQL() { echo $this->processSQL(); } /** * 查看SQL索引。 * Explain sql. * * @param string $sql * @access public * @return void */ public function explain($sql = '') { $sql = empty($sql) ? $this->processSQL() : $sql; $result = $this->dbh->query('explain ' . $sql)->fetch(); a($result); } /** * 处理sql语句,替换表和字段。 * Process the sql, replace the table, fields. * * @access public * @return string the sql string after process. */ public function processSQL() { $sql = $this->sqlobj->get(); /** * 如果是magic模式,处理表和字段。 * If the mode is magic, process the $fields and $table. **/ if($this->mode == 'magic') { if($this->fields == '') $this->fields = '*'; if($this->table == '') $this->app->triggerError('Must set the table name', __FILE__, __LINE__, $exit = true); $sql = sprintf($this->sqlobj->get(), $this->fields, $this->table); } /* If the method if select, update or delete, set the lang condition. */ if($this->autoLang and $this->table != '' and $this->method != 'insert' and $this->method != 'replace') { $lang = $this->app->getClientLang(); /* Get the position to insert lang = ?. */ $wherePOS = strrpos($sql, DAO::WHERE); // The position of WHERE keyword. $groupPOS = strrpos($sql, DAO::GROUPBY); // The position of GROUP BY keyword. $havingPOS = strrpos($sql, DAO::HAVING); // The position of HAVING keyword. $orderPOS = strrpos($sql, DAO::ORDERBY); // The position of ORDERBY keyword. $limitPOS = strrpos($sql, DAO::LIMIT); // The position of LIMIT keyword. $splitPOS = $orderPOS ? $orderPOS : $limitPOS; // If $orderPOS, use it instead of $limitPOS. $splitPOS = $havingPOS? $havingPOS: $splitPOS; // If $havingPOS, use it instead of $orderPOS. $splitPOS = $groupPOS ? $groupPOS : $splitPOS; // If $groupPOS, use it instead of $havingPOS. /* Set the conditon to be appened. */ $tableName = !empty($this->alias) ? $this->alias : $this->table; if(!empty($this->app->config->cn2tw)) $lang = str_replace('zh-tw', 'zh-cn', $lang); $langCondition = " $tableName.lang in('{$lang}', 'all') "; /* If $spliPOS > 0, split the sql at $splitPOS. */ if($splitPOS) { $firstPart = substr($sql, 0, $splitPOS); $lastPart = substr($sql, $splitPOS); if($wherePOS) { $sql = $firstPart . " AND $langCondition " . $lastPart; } else { $sql = $firstPart . " WHERE $langCondition " . $lastPart; } } else { $sql .= $wherePOS ? " AND $langCondition" : " WHERE $langCondition"; } } self::$querys[] = $this->processKeywords($sql); return $sql; } /** * 替换sql常量关键字。 * Process the sql keywords, replace the constants to normal. * * @param string $sql * @access public * @return string the sql string. */ public function processKeywords($sql) { return str_replace(array(DAO::WHERE, DAO::GROUPBY, DAO::HAVING, DAO::ORDERBY, DAO::LIMIT), array('WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT'), $sql); } //-------------------- 查询相关方法(Query related methods) --------------------// /** * 设置$dbh,数据库连接句柄。 * Set the dbh. * * You can use like this: $this->dao->dbh($dbh), thus you can handle two database. * * @param object $dbh * @access public * @return object the dao object self. */ public function dbh($dbh) { $this->dbh = $dbh; return $this; } /** * 执行SQL语句,返回PDOStatement结果集。 * Query the sql, return the statement object. * * @access public * @return object the PDOStatement object. */ public function query($sql = '') { /* 如果有错误,返回一个空的PDOStatement对象,确保后续方法能够执行。*/ /* If any error, return an empty statement object to make sure the remain method to execute. */ if(!empty(dao::$errors)) return new PDOStatement(); if($sql) { $sql = trim($sql); $sqlMethod = strtolower(substr($sql, 0, strpos($sql, ' '))); $this->setMethod($sqlMethod); $this->sqlobj = new sql(); $this->sqlobj->sql = $sql; } else { $sql = $this->processSQL(); } try { $method = $this->method; $this->reset(); if($this->slaveDBH and $method == 'select') { return $this->slaveDBH->query($sql); } else { return $this->dbh->query($sql); } } catch (PDOException $e) { $this->sqlError($e); } } /** * 将记录进行分页,自动设置limit语句。 * Page the records, set the limit part auto. * * @param object $pager * @access public * @return object the dao object self. */ public function page($pager, $distinctField = '') { if(!is_object($pager)) return $this; /* * 如果$pager的总记录为0,需要计算总结果数。 * If the record total is 0, compute it. **/ if($pager->recTotal == 0) { $recTotal = $this->count($distinctField); $pager->setRecTotal($recTotal); $pager->setPageTotal(); } $this->sqlobj->limit($pager->limit()); return $this; } /** * 执行SQL。query()会返回stmt对象,该方法只返回更改或删除的记录数。 * Execute the sql. It's different with query(), which return the stmt object. But this not. * * @param string $sql * @access public * @return int the modified or deleted records. 更改或删除的记录数。 */ public function exec($sql = '') { if(!empty(dao::$errors)) return new PDOStatement(); // If any error, return an empty statement object to make sure the remain method to execute. if($sql) { $this->sqlobj = new sql(); $this->sqlobj->sql = $sql; } else { $sql = $this->processSQL(); } try { if($this->table) unset(dao::$cache[$this->table]); $this->reset(); return $this->dbh->exec($sql); } catch (PDOException $e) { $this->sqlError($e); } } //-------------------- Fetch相关方法(Fetch related methods) -------------------// /** * 获取一个记录。 * Fetch one record. * * @param string $field 如果已经设置获取的字段,则只返回这个字段的值,否则返回这个记录。 * if the field is set, only return the value of this field, else return this record * @access public * @return object|mixed */ public function fetch($field = '') { $sql = $this->processSQL(); $table = $this->table; $key = 'fetch-' . md5($sql); if(isset(dao::$cache[$table][$key])) { if(empty($field)) return $this->getRow(dao::$cache[$table][$key]); $result = dao::$cache[$table][$key]; return $result ? $result->$field : ''; } if(empty($field)) { $data = $this->query()->fetch(); dao::$cache[$table][$key] = $data; return $this->getRow($data); } $this->setFields($field); $result = $this->query()->fetch(PDO::FETCH_OBJ); dao::$cache[$table][$key] = $result; return $result ? $result->$field : ''; } /** * 获取所有记录。 * Fetch all records. * * @param string $keyField 返回以该字段做键的记录 * the key field, thus the return records is keyed by this field * @access public * @return array the records */ public function fetchAll($keyField = '') { $sql = $this->processSQL(); $table = $this->table; $key = 'fetchAll-' . md5($sql . $keyField); if(isset(dao::$cache[$table][$key])) { $rows = dao::$cache[$table][$key]; $result = array(); foreach($rows as $i => $row) $result[$i] = $this->getRow($row); return $result; } $stmt = $this->query(); dao::$cache[$table][$key] = array(); if(empty($keyField)) { $rows = $stmt->fetchAll(); $result = array(); dao::$cache[$table][$key] = $rows; foreach($rows as $i => $row) $result[$i] = $this->getRow($row); return $result; } $rows = array(); while($row = $stmt->fetch()) { dao::$cache[$table][$key][$row->$keyField] = $row; $rows[$row->$keyField] = $this->getRow($row); } return $rows; } /** * 获取所有记录并将按照字段分组。 * Fetch all records and group them by one field. * * @param string $groupField 分组的字段 the field to group by * @param string $keyField 键字段 the field of key * @access public * @return array the records. */ public function fetchGroup($groupField, $keyField = '') { $sql = $this->processSQL(); $table = $this->table; $key = 'fetchGroup-' . md5($sql . $groupField . $keyField); if(isset(dao::$cache[$table][$key])) { $result = array(); $groupRows = dao::$cache[$table][$key]; foreach($groupRows as $groupField => $rows) { foreach($rows as $keyField => $row) $result[$groupField][$keyField] = $this->getRow($row); } return $result; } $stmt = $this->query(); $rows = array(); while($row = $stmt->fetch()) { empty($keyField) ? $rows[$row->$groupField][] = $row : $rows[$row->$groupField][$row->$keyField] = $this->getRow($row); } dao::$cache[$table][$key] = $rows; return $rows; } /** * 获取的记录是以关联数组的形式 * Fetch array like key=>value. * * 如果没有设置参数,用首末两键作为参数。 * If the keyFiled and valueField not set, use the first and last in the record. * * @param string $keyField * @param string $valueField * @access public * @return array */ public function fetchPairs($keyField = '', $valueField = '') { $keyField = trim($keyField, '`'); $valueField = trim($valueField, '`'); $sql = $this->processSQL(); $table = $this->table; $key = 'fetchPairs-' . md5($sql . $keyField . $valueField); if(isset(dao::$cache[$table][$key])) return dao::$cache[$table][$key]; $pairs = array(); $ready = false; $stmt = $this->query(); while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { if(!$ready) { if(empty($keyField)) $keyField = key($row); if(empty($valueField)) { end($row); $valueField = key($row); } $ready = true; } $pairs[$row[$keyField]] = $row[$valueField]; } dao::$cache[$table][$key] = $pairs; return $pairs; } /** * 返回最后插入的ID。 * Return the last insert ID. * * @access public * @return int */ public function lastInsertID() { return $this->dbh->lastInsertID(); } /** * 重新生成数据。 * Get row by data. * * @param array/object $data * @access public * @return array/object */ public function getRow($data) { if(!is_object($data)) return $data; return json_decode(json_encode($data)); } //-------------------- 魔术方法(Magic methods) --------------------// /** * 解析dao的方法名,处理魔术方法。 * Use it to do some convenient queries. * * @param string $funcName the function name to be called * @param array $funcArgs the params * @access public * @return object the dao object self. */ public function __call($funcName, $funcArgs) { $funcName = strtolower($funcName); /* * 如果是findByxxx,转换为where条件语句。 * findByxxx, xxx as will be in the where. **/ if(strpos($funcName, 'findby') !== false) { $this->setMode('magic'); $this->setFields(''); $field = str_replace('findby', '', $funcName); if(count($funcArgs) == 1) { $operator = '='; $value = $funcArgs[0]; } else { $operator = $funcArgs[0]; $value = $funcArgs[1]; } $this->sqlobj = sql::select('%s')->from('%s')->where($field, $operator, $value); return $this; } /* * 获取指定个数的记录:fetch10 获取10条记录。 * Fetch10. **/ elseif(strpos($funcName, 'fetch') !== false) { $max = str_replace('fetch', '', $funcName); $stmt = $this->query(); $rows = array(); $key = isset($funcArgs[0]) ? $funcArgs[0] : ''; $i = 0; while($row = $stmt->fetch()) { $key ? $rows[$row->$key] = $row : $rows[] = $row; $i ++; if($i == $max) break; } return $rows; } /* * 其他的方法,转到sqlobj对象执行。 * Others, call the method in sql class. **/ else { /* * 使用$arg0, $arg1... 生成调用的参数。 * Create the max counts of sql class methods, and then create $arg0, $arg1... **/ for($i = 0; $i < SQL::MAX_ARGS; $i ++) { ${"arg$i"} = isset($funcArgs[$i]) ? $funcArgs[$i] : null; } $this->sqlobj->$funcName($arg0, $arg1, $arg2); return $this; } } //-------------------- 条件检查( Data Checking)--------------------// /** * 检查字段是否满足条件。 * Check a filed is satisfied with the check rule. * * @param string $fieldName the field to check * @param string $funcName the check rule * @param string $condition the condition * @access public * @return object the dao object self. */ public function check($fieldName, $funcName, $condition = '') { /* * 如果没数据中没有该字段,直接返回。 * If no this field in the data, return. **/ if(!isset($this->sqlobj->data->$fieldName)) return $this; /* 设置字段值。 */ /* Set the field label and value. */ global $lang, $config, $app; if(isset($config->db->prefix)) { $table = strtolower(str_replace(array($config->db->prefix, '`'), '', $this->table)); } elseif(strpos($this->table, '_') !== false) { $table = strtolower(substr($this->table, strpos($this->table, '_') + 1)); $table = str_replace('`', '', $table); } else { $table = strtolower($this->table); } $fieldLabel = isset($lang->$table->$fieldName) ? $lang->$table->$fieldName : $fieldName; $value = isset($this->sqlobj->data->$fieldName) ? $this->sqlobj->data->$fieldName : null; /* * 检查唯一性。 * Check unique. **/ if($funcName == 'unique') { $args = func_get_args(); $sql = "SELECT COUNT(*) AS count FROM $this->table WHERE `$fieldName` = " . $this->sqlobj->quote($value); if($condition) $sql .= ' AND ' . $condition; try { $row = $this->dbh->query($sql)->fetch(); if($row->count != 0) $this->logError($funcName, $fieldName, $fieldLabel, array($value)); } catch (PDOException $e) { $this->sqlError($e); } } else { /* * 创建参数。 * Create the params. **/ $funcArgs = func_get_args(); unset($funcArgs[0]); unset($funcArgs[1]); for($i = 0; $i < VALIDATER::MAX_ARGS; $i ++) { ${"arg$i"} = isset($funcArgs[$i + 2]) ? $funcArgs[$i + 2] : null; } $checkFunc = 'check' . $funcName; if(validater::$checkFunc($value, $arg0, $arg1, $arg2) === false) { $this->logError($funcName, $fieldName, $fieldLabel, $funcArgs); } } return $this; } /** * 检查一个字段是否满足条件。 * Check a field, if satisfied with the condition. * * @param string $condition * @param string $fieldName * @param string $funcName * @access public * @return object the dao object self. */ public function checkIF($condition, $fieldName, $funcName) { if(!$condition) return $this; $funcArgs = func_get_args(); for($i = 0; $i < VALIDATER::MAX_ARGS; $i ++) { ${"arg$i"} = isset($funcArgs[$i + 3]) ? $funcArgs[$i + 3] : null; } $this->check($fieldName, $funcName, $arg0, $arg1, $arg2); return $this; } /** * 批量检查字段。 * Batch check some fileds. * * @param string $fields the fields to check, join with , * @param string $funcName * @access public * @return object the dao object self. */ public function batchCheck($fields, $funcName) { $fields = explode(',', str_replace(' ', '', $fields)); $funcArgs = func_get_args(); for($i = 0; $i < VALIDATER::MAX_ARGS; $i ++) { ${"arg$i"} = isset($funcArgs[$i + 2]) ? $funcArgs[$i + 2] : null; } foreach($fields as $fieldName) $this->check($fieldName, $funcName, $arg0, $arg1, $arg2); return $this; } /** * 批量检查字段是否满足条件。 * Batch check fields on the condition is true. * * @param string $condition * @param string $fields * @param string $funcName * @access public * @return object the dao object self. */ public function batchCheckIF($condition, $fields, $funcName) { if(!$condition) return $this; $fields = explode(',', str_replace(' ', '', $fields)); $funcArgs = func_get_args(); for($i = 0; $i < VALIDATER::MAX_ARGS; $i ++) { ${"arg$i"} = isset($funcArgs[$i + 3]) ? $funcArgs[$i + 3] : null; } foreach($fields as $fieldName) $this->check($fieldName, $funcName, $arg0, $arg1, $arg2); return $this; } /** * 根据数据库结构检查字段。 * Check the fields according the the database schema. * * @param string $skipFields fields to skip checking * @access public * @return object the dao object self. */ public function autoCheck($skipFields = '') { $fields = $this->getFieldsType(); $skipFields = ",$skipFields,"; foreach($fields as $fieldName => $validater) { if(strpos($skipFields, $fieldName) !== false) continue; // skip it. if(!isset($this->sqlobj->data->$fieldName)) continue; if($validater['rule'] == 'skip') continue; $options = array(); if(isset($validater['options'])) $options = array_values($validater['options']); for($i = 0; $i < VALIDATER::MAX_ARGS; $i ++) { ${"arg$i"} = isset($options[$i]) ? $options[$i] : null; } $this->check($fieldName, $validater['rule'], $arg0, $arg1, $arg2); } return $this; } /** * 记录错误到日志。 * Log the error. * * module/common/lang中定义了错误提示信息。 * For the error notice, see module/common/lang. * * @param string $checkType the check rule * @param string $fieldName the field name * @param string $fieldLabel the field label * @param array $funcArgs the args * @access public * @return void */ public function logError($checkType, $fieldName, $fieldLabel, $funcArgs = array()) { global $lang; $error = $lang->error->$checkType; $replaces = array_merge(array($fieldLabel), $funcArgs); // the replace values. /* * 如果$error错误信息是一个字符串,进行替换。 * Just a string, cycle the $replaces. **/ if(!is_array($error)) { foreach($replaces as $replace) { $pos = strpos($error, '%s'); if($pos === false) break; $error = substr($error, 0, $pos) . $replace . substr($error, $pos + 2); } } /* * 如果error错误信息是一个数组,选择一个%s满足替换个数的进行替换。 * If the error define is an array, select the one which %s counts match the $replaces. **/ else { /* * 去掉空值项。 * Remove the empty items. **/ foreach($replaces as $key => $value) if(is_null($value)) unset($replaces[$key]); $replacesCount = count($replaces); foreach($error as $errorString) { if(substr_count($errorString, '%s') == $replacesCount) { $error = vsprintf($errorString, $replaces); } } } dao::$errors[$fieldName][] = $error; } /** * 判断是否有错误。 * Judge any error or not. * * @access public * @return bool */ public static function isError() { return !empty(dao::$errors); } /** * 获取错误。 * Get the errors. * * @access public * @return array */ public static function getError($join = false) { $errors = dao::$errors; dao::$errors = array(); // 清除dao的错误信息(Must clear errors) if(!$join) return $errors; if(is_array($errors)) { $message = ''; foreach($errors as $item) { is_array($item) ? $message .= join('\n', $item) . '\n' : $message .= $item . '\n'; } return $message; } } /** * 获取表的字段类型。 * Get the defination of fields of the table. * * @access public * @return array */ public function getFieldsType() { try { $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); $sql = "DESC $this->table"; $rawFields = $this->dbh->query($sql)->fetchAll(); $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); } catch (PDOException $e) { $this->sqlError($e); } foreach($rawFields as $rawField) { $firstPOS = strpos($rawField->type, '('); $type = substr($rawField->type, 0, $firstPOS > 0 ? $firstPOS : strlen($rawField->type)); $type = str_replace(array('big', 'small', 'medium', 'tiny', 'var'), '', $type); $field = array(); if($type == 'enum' or $type == 'set') { $rangeBegin = $firstPOS + 2; // 移除开始的引用符 Remove the first quote. $rangeEnd = strrpos($rawField->type, ')') - 1; // 移除结束的引用符 Remove the last quote. $range = substr($rawField->type, $rangeBegin, $rangeEnd - $rangeBegin); $field['rule'] = 'reg'; $field['options']['reg'] = '/' . str_replace("','", '|', $range) . '/'; } elseif($type == 'char') { $begin = $firstPOS + 1; $end = strpos($rawField->type, ')', $begin); $length = substr($rawField->type, $begin, $end - $begin); $field['rule'] = 'length'; $field['options']['max'] = $length; $field['options']['min'] = 0; } elseif($type == 'int') { $field['rule'] = 'int'; } elseif($type == 'float' or $type == 'double') { $field['rule'] = 'float'; } elseif($type == 'date') { $field['rule'] = 'date'; } else { $field['rule'] = 'skip'; } $fields[$rawField->field] = $field; } return $fields; } /** * Process SQL error by code. * * @param object $exception * @access public * @return void */ public function sqlError($exception) { $errorInfo = $exception->errorInfo; $errorCode = $errorInfo[1]; $errorMsg = $errorInfo[2]; $message = $exception->getMessage(); if(strpos($this->repairCode, "|$errorCode|") !== false or ($errorCode == '1016' and strpos($errorMsg, 'errno: 145') !== false) or strpos($message, 'repair') !== false) { global $config; if(isset($config->framework->autoRepairTable) and $config->framework->autoRepairTable) die(js::locate($config->webRoot . 'checktable.php', 'top')); $message .= ' ' . $this->lang->repairTable; } $sql = $this->sqlobj->get(); $this->app->triggerError($message . "

The sql is: $sql

", __FILE__, __LINE__, $exit = true); } } /** * SQL类。 * The SQL class. * * @package framework */ class baseSQL { /** * 所有方法的最大参数个数。 * The max count of params of all methods. * */ const MAX_ARGS = 3; /** * SQL字符串。 * The sql string. * * @var string * @access public */ public $sql = ''; /** * 全局变量$dbh。 * The global $dbh. * * @var object * @access public */ public $dbh; /** * 更新或插入日期。 * The data to update or insert. * * @var mix * @access public */ public $data; /** * 是否是第一次设置。 * Is the first time to call set. * * @var bool * @access public; */ public $isFirstSet = true; /** * 是否是在条件语句中。 * If in the logic of judge condition or not. * * @var bool * @access public; */ public $inCondition = false; /** * 条件是否为真。 * The condition is true or not. * * @var bool * @access public; */ public $conditionIsTrue = false; /** * WHERE条件嵌套小括号标记。 * If in mark or not. * * @var bool * @access public; */ public $inMark = false; /** * 是否开启特殊字符转义。 * Magic quote or not. * * @var bool * @access public */ public $magicQuote; /** * 构造方法。 * The construct function. * * @access public * @return void */ public function __construct($table = '') { global $dbh; $this->dbh = $dbh; $this->magicQuote = (version_compare(phpversion(), '5.4', '<') and function_exists('get_magic_quotes_gpc') and get_magic_quotes_gpc()); } /** * 工厂方法。 * The factory method. * * @param string $table * @access public * @return object the sql object. */ public static function factory($table = '') { return new sql($table); } /** * select语句。 * The sql is select. * * @param string $field * @access public * @return object the sql object. */ public static function select($field = '*') { $sqlobj = self::factory(); $sqlobj->sql = "SELECT $field "; return $sqlobj; } /** * update语句。 * The sql is update. * * @param string $table * @access public * @return object the sql object. */ public static function update($table) { $sqlobj = self::factory(); $sqlobj->sql = "UPDATE $table SET "; return $sqlobj; } /** * insert语句。 * The sql is insert. * * @param string $table * @access public * @return object the sql object. */ public static function insert($table) { $sqlobj = self::factory(); $sqlobj->sql = "INSERT INTO $table SET "; return $sqlobj; } /** * replace语句。 * The sql is replace. * * @param string $table * @access public * @return object the sql object. */ public static function replace($table) { $sqlobj = self::factory(); $sqlobj->sql = "REPLACE $table SET "; return $sqlobj; } /** * delete语句。 * The sql is delete. * * @access public * @return object the sql object. */ public static function delete() { $sqlobj = self::factory(); $sqlobj->sql = "DELETE "; return $sqlobj; } /** * 将关联数组转换为sql语句中 `key` = value 的形式。 * Join the data items by key = value. * * @param object $data * @param string $skipFields the fields to skip. * @access public * @return object the sql object. */ public function data($data, $skipFields = '') { $data = (object) $data; if($skipFields) $skipFields = ',' . str_replace(' ', '', $skipFields) . ','; foreach($data as $field => $value) { if(!preg_match('|^\w+$|', $field)) { unset($data->$field); continue; } if(strpos($skipFields, ",$field,") !== false) continue; $this->sql .= "`$field` = " . $this->quote($value) . ','; } $this->data = $data; $this->sql = rtrim($this->sql, ','); // Remove the last ','. return $this; } /** * 在左边添加'('。 * Add an '(' at left. * * @param int $count * @access public * @return object the sql object. */ public function markLeft($count = 1) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= str_repeat('(', $count); $this->inMark = true; return $this; } /** * 在右边增加')'。 * Add an ')' at right. * * @param int $count * @access public * @return object the sql object. */ public function markRight($count = 1) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= str_repeat(')', $count); $this->inMark = false; return $this; } /** * SET部分。 * The set part. * * @param string $set * @access public * @return object the sql object. */ public function set($set) { /* Add ` to avoid keywords of mysql. */ if(strpos($set, '=') ===false) { $set = str_replace(',', '', $set); $set = '`' . str_replace('`', '', $set) . '`'; } $this->sql .= $this->isFirstSet ? " $set" : ", $set"; if($this->isFirstSet) $this->isFirstSet = false; return $this; } /** * 创建From部分。 * Create the from part. * * @param string $table * @access public * @return object the sql object. */ public function from($table) { $this->sql .= "FROM $table"; return $this; } /** * 创建Alias部分,Alias转为AS。 * Create the Alias part. * * @param string $alias * @access public * @return object the sql object. */ public function alias($alias) { $this->sql .= " AS $alias "; return $this; } /** * 创建LEFT JOIN部分。 * Create the left join part. * * @param string $table * @access public * @return object the sql object. */ public function leftJoin($table) { $this->sql .= " LEFT JOIN $table"; return $this; } /** * 创建ON部分。 * Create the on part. * * @param string $condition * @access public * @return object the sql object. */ public function on($condition) { $this->sql .= " ON $condition "; return $this; } /** * 开始条件判断。 * Begin condition judge. * * @param bool $condition * @access public * @return object the sql object. */ public function beginIF($condition) { $this->inCondition = true; $this->conditionIsTrue = $condition; return $this; } /** * 结束条件判断。 * End the condition judge. * * @access public * @return object the sql object. */ public function fi() { $this->inCondition = false; $this->conditionIsTrue = false; return $this; } /** * 创建WHERE部分。 * Create the where part. * * @param string $arg1 the field name * @param string $arg2 the operator * @param string $arg3 the value * @access public * @return object the sql object. */ public function where($arg1, $arg2 = null, $arg3 = null) { if($this->inCondition and !$this->conditionIsTrue) return $this; if($arg3 !== null) { $value = $this->quote($arg3); $condition = "`$arg1` $arg2 " . $this->quote($arg3); } else { $condition = $arg1; } if(!$this->inMark) $this->sql .= ' ' . DAO::WHERE ." $condition "; if($this->inMark) $this->sql .= " $condition "; return $this; } /** * 创建AND部分。 * Create the AND part. * * @param string $condition * @access public * @return object the sql object. */ public function andWhere($condition, $addMark = false) { if($this->inCondition and !$this->conditionIsTrue) return $this; $mark = $addMark ? '(' : ''; $this->sql .= " AND {$mark} $condition "; return $this; } /** * 创建OR部分。 * Create the OR part. * * @param bool $condition * @access public * @return object the sql object. */ public function orWhere($condition) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " OR $condition "; return $this; } /** * 创建'='部分。 * Create the '='. * * @param string $value * @access public * @return object the sql object. */ public function eq($value) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " = " . $this->quote($value); return $this; } /** * 创建'!='。 * Create '!='. * * @param string $value * @access public * @return void the sql object. */ public function ne($value) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " != " . $this->quote($value); return $this; } /** * 创建'>'。 * Create '>'. * * @param string $value * @access public * @return object the sql object. */ public function gt($value) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " > " . $this->quote($value); return $this; } /** * 创建'>=' * Create '>='. * * @param string $value * @access public * @return object the sql object. */ public function ge($value) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " >= " . $this->quote($value); return $this; } /** * 创建'<'。 * Create '<'. * * @param mixed $value * @access public * @return object the sql object. */ public function lt($value) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " < " . $this->quote($value); return $this; } /** * 创建 '<='。 * Create '<='. * * @param mixed $value * @access public * @return object the sql object. */ public function le($value) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " <= " . $this->quote($value); return $this; } /** * 创建"between and"。 * Create "between and" * * @param string $min * @param string $max * @access public * @return object the sql object. */ public function between($min, $max) { if($this->inCondition and !$this->conditionIsTrue) return $this; $min = $this->quote($min); $max = $this->quote($max); $this->sql .= " BETWEEN $min AND $max "; return $this; } /** * 创建IN部分。 * Create in part. * * @param string|array $ids ','分割的字符串或者数组 list string by ',' or an array * @access public * @return object the sql object. */ public function in($ids) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= helper::dbIN($ids); return $this; } /** * 创建'NOT IN'部分。 * Create not in part. * * @param string|array $ids list string by ',' or an array * @access public * @return object the sql object. */ public function notin($ids) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= ' NOT ' . helper::dbIN($ids); return $this; } /** * 创建LIKE部分。 * Create the like by part. * * @param string $string * @access public * @return object the sql object. */ public function like($string) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= " LIKE " . $this->quote($string); return $this; } /** * 创建NOT LIKE部分。 * Create the not like by part. * * @param string $string * @access public * @return object the sql object. */ public function notLike($string) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= "NOT LIKE " . $this->quote($string); return $this; } /** * 创建ORDER BY部分。 * Create the order by part. * * @param string $order * @access public * @return object the sql object. */ public function orderBy($order) { if($this->inCondition and !$this->conditionIsTrue) return $this; $order = str_replace(array('|', '', '_'), ' ', $order); /* Add "`" in order string. */ /* When order has limit string. */ $pos = stripos($order, 'limit'); $orders = $pos ? substr($order, 0, $pos) : $order; $limit = $pos ? substr($order, $pos) : ''; if($limit and !preg_match('/^[0-9]+ *(, *[0-9]+)?$/', $limit)) $limit = ''; $orders = trim($orders); if(empty($orders)) return $this; if(!preg_match('/^(\w+\.)?(`\w+`|\w+)( +(desc|asc))?( *(, *(\w+\.)?(`\w+`|\w+)( +(desc|asc))?)?)*$/i', $orders)) die("Order is bad request, The order is $orders"); $orders = explode(',', $orders); foreach($orders as $i => $order) { $orderParse = explode(' ', trim($order)); foreach($orderParse as $key => $value) { $value = trim($value); if(empty($value) or strtolower($value) == 'desc' or strtolower($value) == 'asc') continue; $field = $value; /* such as t1.id field. */ if(strpos($value, '.') !== false) list($table, $field) = explode('.', $field); if(strpos($field, '`') === false) $field = "`$field`"; $orderParse[$key] = isset($table) ? $table . '.' . $field : $field; unset($table); } $orders[$i] = join(' ', $orderParse); if(empty($orders[$i])) unset($orders[$i]); } $order = join(',', $orders) . ' ' . $limit; $this->sql .= ' ' . DAO::ORDERBY . " $order"; return $this; } /** * 创建LIMIT部分。 * Create the limit part. * * @param string $limit * @access public * @return object the sql object. */ public function limit($limit) { if($this->inCondition and !$this->conditionIsTrue) return $this; if(empty($limit)) return $this; /* filter limit. */ $limit = trim(str_ireplace('limit', '', $limit)); if(!preg_match('/^[0-9]+ *(, *[0-9]+)?$/', $limit)) die("Limit is bad query, The limit is $limit"); $this->sql .= ' ' . DAO::LIMIT . " $limit "; return $this; } /** * 创建GROUP BY部分。 * Create the groupby part. * * @param string $groupBy * @access public * @return object the sql object. */ public function groupBy($groupBy) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= ' ' . DAO::GROUPBY . " $groupBy"; return $this; } /** * 创建HAVING部分。 * Create the having part. * * @param string $having * @access public * @return object the sql object. */ public function having($having) { if($this->inCondition and !$this->conditionIsTrue) return $this; $this->sql .= ' ' . DAO::HAVING . " $having"; return $this; } /** * 获取SQL字符串。 * Get the sql string. * * @access public * @return string */ public function get() { return $this->sql; } /** * 对字段加转义。 * Quote a var. * * @param mixed $value * @access public * @return mixed */ public function quote($value) { if($this->magicQuote) $value = stripslashes($value); return $this->dbh->quote((string)$value); } } ================================================ FILE: lib/base/filter/filter.class.php ================================================ array('min_range' => $args[1], 'max_range' => $args[2])); } else { $options = array('options' => array('min_range' => $args[1])); } return filter_var($var, FILTER_VALIDATE_INT, $options); } else { return filter_var($var, FILTER_VALIDATE_INT); } } /** * 检查不是Int类型。 * Not int checking. * * @param int $var * @static * @access public * @return bool */ public static function checkNotInt($var) { return !self::checkInt($var); } /** * 检查Float类型。 * Float checking. * * @param float $var * @param string $decimal * @static * @access public * @return bool */ public static function checkFloat($var, $decimal = '.') { return filter_var($var, FILTER_VALIDATE_FLOAT, array('options' => array('decimal' => $decimal))); } /** * 检查Email。 * Email checking. * * @param string $var * @static * @access public * @return bool */ public static function checkEmail($var) { return filter_var($var, FILTER_VALIDATE_EMAIL); } /** * 检查电话或手机号码 * Check phone number. * * @param string $var * @static * @access public * @return void */ public static function checkPhone($var) { return (validater::checkTel($var) or validater::checkMobile($var)); } /** * 检查电话号码 * Check tel number. * * @param int $var * @static * @access public * @return void */ public static function checkTel($var) { return preg_match("/^([0-9]{3,4}-?)?[0-9]{7,8}$/", $var); } /** * 检查手机号码 * Check mobile number. * * @param string $var * @static * @access public * @return void */ public static function checkMobile($var) { return preg_match("/^1[3-5,7,8]{1}[0-9]{9}$/", $var); } /** * 检查网址。 * 该规则不支持中文字符的网址。 * * URL checking. * The check rule of filter don't support chinese. * * @param string $var * @static * @access public * @return bool */ public static function checkURL($var) { return filter_var($var, FILTER_VALIDATE_URL); } /** * 检查域名,不支持中文。 * Domain checking. * * The check rule of filter don't support chinese. * * @param string $var * @static * @access public * @return bool */ public static function checkDomain($var) { return preg_match('/^([a-z0-9-]+\.)+[a-z]{2,15}$/', $var); } /** * 检查IP地址。 * IP checking. * * @param ip $var * @param string $range all|public|static|private * @static * @access public * @return bool */ public static function checkIP($var, $range = 'all') { if($range == 'all') return filter_var($var, FILTER_VALIDATE_IP); if($range == 'public static') return filter_var($var, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); if($range == 'private') { if($var == '127.0.0.1' or filter_var($var, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false) return true; return false; } } /** * 身份证号检查。 * Idcard checking. * * @access public * @return void */ public static function checkIdcard($idcard) { if(strlen($idcard)!=18) return false; $idcard = strtoupper($idcard); $cityList = array( '11','12','13','14','15','21','22', '23','31','32','33','34','35','36', '37','41','42','43','44','45','46', '50','51','52','53','54','61','62', '63','64','65','71','81','82','91' ); if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $idcard)) return false; if (!in_array(substr($idcard, 0, 2), $cityList)) return false; $baseCode = substr($idcard, 0, 17); $verifyCode = substr($idcard, 17, 1); $interference = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); $verifyConfig = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); $total = 0; for($i=0; $i<17; $i++) $total += substr($baseCode, $i, 1) * $interference[$i]; $mod = $total % 11; return $verifyCode == $verifyConfig[$mod]; } /** * 日期检查。注意,2009-09-31是一个合法日期,系统会将它转换为2009-10-01。 * Date checking. Note: 2009-09-31 will be an valid date, because strtotime auto fixed it to 10-01. * * @param date $date * @static * @access public * @return bool */ public static function checkDate($date) { if($date == '0000-00-00') return true; $stamp = strtotime($date); if(!is_numeric($stamp)) return false; return checkdate(date('m', $stamp), date('d', $stamp), date('Y', $stamp)); } /** * 检查正则表达式。 * REG checking. * * @param string $var * @param string $reg * @static * @access public * @return bool */ public static function checkREG($var, $reg) { return filter_var($var, FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $reg))); } /** * 检查长度。 * Length checking. * * @param string $var * @param string $max * @param int $min * @static * @access public * @return bool */ public static function checkLength($var, $max, $min = 0) { $length = function_exists('mb_strlen') ? mb_strlen($var, 'utf-8') : strlen($var); return self::checkInt($length, $min, $max); } /** * 检查不为空。 * Not empty checking. * * @param mixed $var * @static * @access public * @return bool */ public static function checkNotEmpty($var) { return !empty($var); } /** * 检查为空。 * Empty checking. * * @param mixed $var * @static * @access public * @return bool */ public static function checkEmpty($var) { return empty($var); } /** * 检查用户名。 * Account checking. * * @param string $var * @static * @access public * @return bool */ public static function checkAccount($var) { global $config; $accountRule = empty($config->accountRule) ? '|^[a-zA-Z0-9_]{1}[a-zA-Z0-9_\.]{1,}[a-zA-Z0-9_]{1}$|' : $config->accountRule; return self::checkREG($var, $accountRule); } /** * 检查Code。 * Check code. * * @param string $var * @static * @access public * @return bool */ public static function checkCode($var) { return self::checkREG($var, '|^[A-Za-z0-9]+$|'); } /** * 检查验证码。 * Check captcha. * * @param mixed $var * @static * @access public * @return bool */ public static function checkCaptcha($var) { if(!isset($_SESSION['captcha'])) return false; return $var == $_SESSION['captcha']; } /** * 是否等于给定的值。 * Must equal a value. * * @param mixed $var * @param mixed $value * @static * @access public * @return bool */ public static function checkEqual($var, $value) { return $var == $value; } /** * 检查不等于给定的值 * Must not equal a value. * * @param mixed $var * @param mixed $value * @static * @access public * @return bool */ public static function checkNotEqual($var, $value) { return $var != $value; } /** * 检查大于给定的值。 * Must greater than a value. * * @param mixed $var * @param mixed $value * @static * @access public * @return bool */ public static function checkGT($var, $value) { return $var > $value; } /** * 检查小于给定的值 * Must less than a value. * * @param mixed $var * @param mixed $value * @static * @access public * @return bool */ public static function checkLT($var, $value) { return $var < $value; } /** * 检查大于等于给定的值 * Must greater than a value or equal a value. * * @param mixed $var * @param mixed $value * @static * @access public * @return bool */ public static function checkGE($var, $value) { return $var >= $value; } /** * 检查小于等于给定的值 * Must less than a value or equal a value. * * @param mixed $var * @param mixed $value * @static * @access public * @return bool */ public static function checkLE($var, $value) { return $var <= $value; } /** * 检查是否在给定的列表里面。 * Must in value list. * * @param mixed $var * @param mixed $value * @static * @access public * @return bool */ public static function checkIn($var, $value) { if(!is_array($value)) $value = explode(',', $value); return in_array($var, $value); } /** * 检查文件名。 * Check file name. * * @param string $var * @static * @access public * @return bool */ public static function checkFileName($var) { return !preg_match('/>+|:+|<+/', $var); } /** * 检查敏感词。 * Check sensitive words. * * @param object $vars * @param array $dicts * @static * @access public * @return void */ public static function checkSensitive($vars, $dicts) { foreach($vars as $var) { if(!$var) continue; foreach($dicts as $dict) { if(strpos($var, $dict) === false) continue; if(strpos($var, $dict) !== false) return false; } } return true; } /** * 过滤附件。 * Filter files. * * @access public * @return array */ public static function filterFiles() { global $config; if(empty($_FILES)) return $_FILES; foreach($_FILES as $varName => $files) { if(is_array($files['name'])) { foreach($files['name'] as $i => $fileName) { $extension = ltrim(strrchr($fileName, '.'), '.'); if(stripos(",{$config->file->dangers},", ",{$extension},") !== false) { unset($_FILES); return array(); } } } else { $extension = ltrim(strrchr($files['name'], '.'), '.'); if(stripos(",{$config->file->dangers},", ",{$extension},") !== false) { unset($_FILES); return array(); } } } return $_FILES; } /** * 过滤超级变量。 * Filter super vars. * * @param array $super * @access public * @return array */ public static function filterSuper($super) { if(!is_array($super)) return $super; $super = self::filterBadKeys($super); foreach($super as $key => $item) { if(is_array($item)) { $item = self::filterBadKeys($item); foreach($item as $subkey => $subItem) { if(is_array($subItem)) continue; $subItem = self::filterTrojan($subItem); $super[$key][$subkey] = self::filterXSS($subItem); } } else { $item = self::filterTrojan($item); $super[$key] = self::filterXSS($item); } } return $super; } /** * 过滤不符合规则的键值。 * Filter bad keys. * * @param mix $var * @access public * @return mix */ public static function filterBadKeys($var) { global $config; if(empty($config->framework->filterBadKeys)) return $var; foreach($var as $key => $value) if(preg_match('/[^a-zA-Z0-9_\.\-]/', $key)) unset($var[$key]); return $var; } /** * 过滤木马代码。 * Filter trojan codes. * * @param string $var * @access public * @return string */ public static function filterTrojan($var) { global $config; if(empty($config->framework->filterTrojan)) return $var; if(strpos(htmlspecialchars_decode($var), 'framework->filterXSS)) return $var; if(stripos($var, 'getModuleName(); $methodName = $app->getMethodName(); $params = $app->getParams(); if($type == 'cookie') { $pagerCookie = 'pager' . ucfirst($moduleName) . ucfirst($methodName); $filter->default->cookie[$pagerCookie] = 'int'; } foreach($var as $key => $value) { if($config->requestType == 'GET' and $type == 'get' and isset($params[$key])) continue; $rules = ''; if(isset($filter->{$moduleName}->{$methodName}->{$type}[$key])) { $rules = $filter->{$moduleName}->{$methodName}->{$type}[$key]; } elseif(isset($filter->{$moduleName}->default->{$type}[$key])) { $rules = $filter->{$moduleName}->default->{$type}[$key]; } elseif(isset($filter->default->{$type}[$key])) { $rules = $filter->default->{$type}[$key]; } if(!self::checkByRule($value, $rules)) unset($var[$key]); } return $var; } /** * Check by rule. * * @param string $var * @param string $rule like: int account reg::md5 reg::/^[a-zA-Z0-9]+$/. * @static * @access public * @return bool */ public static function checkByRule($var, $rule) { if(empty($rule)) return false; /* Parse rule to operator and param. */ list($operator, $param) = baseValidater::parseRuleString($rule); /* check by operator. */ $checkMethod = 'check' . $operator; if(method_exists('baseValidater', $checkMethod)) { if(empty($param) and self::$checkMethod($var) === false) return false; if(!empty($param) and self::$checkMethod($var, $param) === false) return false; } elseif(function_exists('is_' . $operator)) { $checkFunction = 'is_' . $operator; if(!$checkFunction($var)) return false; } else { return false; } return true; } /** * Parse rule string. * * @param string $rule like: int account reg::md5 reg::/^[a-zA-Z0-9]+$/. * @static * @access public * @return array */ public static function parseRuleString($rule) { global $filter; if(strpos($rule, '::') !== false) list($operator, $param) = explode('::', $rule); if(strpos($rule,'::') === false) list($operator, $param) = array($rule, ''); if($operator == 'reg' and isset($filter->rules->$param)) $param = $filter->rules->$param; return array($operator, $param); } /** * 调用一个方法进行检查。 * Call a function to check it. * * @param mixed $var * @param string $func * @static * @access public * @return bool */ public static function call($var, $func) { return filter_var($var, FILTER_CALLBACK, array('options' => $func)); } } /** * fixer类,处理数据。 * fixer class, to fix data types. * * @package framework */ class baseFixer { /** * 处理的数据。 * The data to be fixed. * * @var object * @access public */ public $data; /** * 跳过处理的字段。 * The fields to striped. * * @var array * @access public */ public $stripedFields = array(); /** * 构造方法,将超级全局变量转换为对象。 * The construction function, according the scope, convert it to object. * * @param string $scope the scope of the var, should be post|get|server|session|cookie|env * @access public * @return void */ public function __construct($scope) { switch($scope) { case 'post': $this->data = (object)$_POST; break; case 'server': $this->data = (object)$_SERVER; break; case 'get': $this->data = (object)$_GET; break; case 'session': $this->data = (object)$_SESSION; break; case 'cookie': $this->data = (object)$_COOKIE; break; case 'env': $this->data = (object)$_ENV; break; case 'file': $this->data = (object)$_FILES; break; default: die('scope not supported, should be post|get|server|session|cookie|env'); } } /** * 工厂方法。 * The factory function. * * @param string $scope * @access public * @return object fixer object. */ public static function input($scope) { return new fixer($scope); } /** * 处理Email。 * Email fix. * * @param string $fieldName * @access public * @return object fixer object. */ public function cleanEmail($fieldName) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_EMAIL); return $this; } /** * url编码。 * urlencode. * * @param string $fieldName * @access public * @return object fixer object. */ public function encodeURL($fieldName) { $fields = $this->processFields($fieldName); $args = func_get_args(); foreach($fields as $fieldName) { $this->data->$fieldName = isset($args[1]) ? filter_var($this->data->$fieldName, FILTER_SANITIZE_ENCODED, $args[1]) : filter_var($this->data->$fieldName, FILTER_SANITIZE_ENCODED); } return $this; } /** * 清理网址。 * Clean the url. * * @param string $fieldName * @access public * @return object fixer object. */ public function cleanURL($fieldName) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_URL); return $this; } /** * 处理Float类型。 * Float fixer. * * @param string $fieldName * @access public * @return object fixer object. */ public function cleanFloat($fieldName) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION|FILTER_FLAG_ALLOW_THOUSAND); return $this; } /** * 处理Int类型。 * Int fixer. * * @param string $fieldName * @access public * @return object fixer object. */ public function cleanINT($fieldName = '') { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_NUMBER_INT); return $this; } /** * 将字符串转换为可以在浏览器查看的编码。 * Special chars. * * @param string $fieldName * @access public * @return object fixer object */ public function specialChars($fieldName) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) { if(empty($this->stripedFields) or !in_array($fieldName, $this->stripedFields)) $this->data->$fieldName = $this->specialArray($this->data->$fieldName); } return $this; } /** * Special array * * @param mix $data * @access public * @return mix */ public function specialArray($data) { if(!is_array($data)) return htmlspecialchars($data, ENT_QUOTES); foreach($data as &$value) $value = $this->specialArray($value); return $data; } /** * 忽略该标签。 * Strip tags * * @param string $fieldName * @param string $allowableTags * @access public * @return object fixer object */ public function stripTags($fieldName, $allowedTags = '') { global $app, $config; if(empty($allowedTags) and isset($config->allowedTags)) $allowedTags = $config->allowedTags; $usePurifier = isset($config->framework->purifier) ? $config->framework->purifier : false; if($usePurifier) { $app->loadClass('purifier', true); $purifierConfig = HTMLPurifier_Config::createDefault(); $purifierConfig->set('Filter.YouTube', 1); /* Disable caching. */ $purifierConfig->set('Cache.DefinitionImpl', null); $purifier = new HTMLPurifier($purifierConfig); $def = $purifierConfig->getHTMLDefinition(true); $def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top'); } $fields = $this->processFields($fieldName); foreach($fields as $fieldName) { if(function_exists('get_magic_quotes_gpc') and get_magic_quotes_gpc()) $this->data->$fieldName = stripslashes($this->data->$fieldName); if(!in_array($fieldName, $this->stripedFields)) { if(!defined('RUN_MODE') or RUN_MODE != 'admin') { /* * purifier会把 替换空格,kindeditor在会吧行首的空格去掉。 * purifier will change   to ' ', and edit it will no space in line head use kindeditor. **/ $this->data->$fieldName = preg_replace('/<[^>]+data->$fieldName); $this->data->$fieldName = preg_replace('/>[^<]+>/', '>', $this->data->$fieldName); if($usePurifier) $this->data->$fieldName = str_replace(' ', '&spnb;', $this->data->$fieldName); $this->data->$fieldName = $usePurifier ? $purifier->purify($this->data->$fieldName) : strip_tags($this->data->$fieldName, $allowedTags); if($usePurifier) $this->data->$fieldName = str_replace('&spnb;', ' ', $this->data->$fieldName); } } $this->stripedFields[] = $fieldName; } return $this; } /** * 忽略处理给定的字段。 * Skip special chars check. * * @param string $filename * @access public * @return object fixer object */ public function skipSpecial($fieldName) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) $this->stripedFields[] = $fieldName; return $this; } /** * 给字段添加引用,防止字符与关键字冲突。 * Quote * * @param string $fieldName * @access public * @return object fixer object */ public function quote($fieldName) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_MAGIC_QUOTES); return $this; } /** * 设置字段的默认值。 * Set default value of some fileds. * * @param string $fields * @param mixed $value * @access public * @return object fixer object */ public function setDefault($fields, $value) { $fields = strpos($fields, ',') ? explode(',', str_replace(' ', '', $fields)) : array($fields); foreach($fields as $fieldName)if(!isset($this->data->$fieldName) or empty($this->data->$fieldName)) $this->data->$fieldName = $value; return $this; } /** * 如果条件为真,则为字段赋值。 * Set value of a filed on the condition is true. * * @param bool $condition * @param string $fieldName * @param string $value * @access public * @return object fixer object */ public function setIF($condition, $fieldName, $value) { if($condition) $this->data->$fieldName = $value; return $this; } /** * 强制给字段赋值。 * Set the value of a filed in force. * * @param string $fieldName * @param mixed $value * @access public * @return object fixer object */ public function setForce($fieldName, $value) { $this->data->$fieldName = $value; return $this; } /** * 移除一个字段。 * Remove a field. * * @param string $fieldName * @access public * @return object fixer object */ public function remove($fieldName) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) unset($this->data->$fieldName); return $this; } /** * 如果条件为真,移除该字段。 * Remove a filed on the condition is true. * * @param bool $condition * @param string $fields * @access public * @return object fixer object */ public function removeIF($condition, $fields) { $fields = $this->processFields($fields); if($condition) foreach($fields as $fieldName) unset($this->data->$fieldName); return $this; } /** * 为数据添加新的项。 * Add an item to the data. * * @param string $fieldName * @param mixed $value * @access public * @return object fixer object */ public function add($fieldName, $value) { $this->data->$fieldName = $value; return $this; } /** * 如果条件为真,则为数据添加新的项。 * Add an item to the data on the condition if true. * * @param bool $condition * @param string $fieldName * @param mixed $value * @access public * @return object fixer object */ public function addIF($condition, $fieldName, $value) { if($condition) $this->data->$fieldName = $value; return $this; } /** * 为指定字段增加值。 * Join the field. * * @param string $fieldName * @param string $value * @access public * @return object fixer object */ public function join($fieldName, $value) { if(!isset($this->data->$fieldName) or !is_array($this->data->$fieldName)) return $this; $this->data->$fieldName = join($value, $this->data->$fieldName); return $this; } /** * 调用一个方法来处理数据。 * Call a function to fix it. * * @param string $fieldName * @param string $func * @access public * @return object fixer object */ public function callFunc($fieldName, $func) { $fields = $this->processFields($fieldName); foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_CALLBACK, array('options' => $func)); return $this; } /** * 处理完成后返回数据。 * Get the data after fixing. * * @param string $fieldName * @access public * @return object */ public function get($fields = '') { $fields = str_replace(' ', '', trim($fields)); foreach($this->data as $field => $value) $this->specialChars($field); if(empty($fields)) return $this->data; if(strpos($fields, ',') === false) return $this->data->$fields; $fields = array_flip(explode(',', $fields)); foreach($this->data as $field => $value) { if(!isset($fields[$field])) unset($this->data->$field); if(!in_array($field, $this->stripedFields)) $this->specialChars($field); } return $this->data; } /** * 处理字段,如果字段中含有',',拆分成数组。如果字段不在$data中,删除掉。 * Process fields, if contains ',', split it to array. If not in $data, remove it. * * @param string $fields * @access public * @return array */ public function processFields($fields) { $fields = strpos($fields, ',') ? explode(',', str_replace(' ', '', $fields)) : array($fields); foreach($fields as $key => $fieldName) if(!isset($this->data->$fieldName)) unset($fields[$key]); return $fields; } } ================================================ FILE: lib/base/front/front.class.php ================================================ $title\n"; } /** * 生成meta标签。 * Create a meta. * * @param mixed $name the meta name * @param mixed $value the meta value * @static * @access public * @return string */ public static function meta($name, $value) { if($name == 'charset') return "\n"; return "\n"; } /** * 生成favicon标签。 * Create favicon tag * * @param mixed $url the url of the icon. * @static * @access public * @return string */ public static function favicon($url) { return "\n\n"; } /** * 创建图标。 * Create icon. * * @param name $name the name of the icon. * @param cssClass $class the extra css class of the icon. * @static * @access public * @return string */ public static function icon($name, $class = '') { $class = empty($class) ? ('icon-' . $name) : ('icon-' . $name . ' ' . $class); return ""; } /** * 生成rss标签。 * Create the rss tag. * * @param string $url * @param string $title * @static * @access public * @return string */ public static function rss($url, $title = '') { return ""; } /** * 生成超链接。 * Create tags like text * * @param string $href the link url. * @param string $title the link title. * @param string $misc other params. * @param string $newline * @static * @access public * @return string */ static public function a($href = '', $title = '', $misc = '', $newline = true) { global $config; if(empty($title)) $title = $href; $newline = $newline ? "\n" : ''; return "$title$newline"; } /** * 生成邮件链接。 * Create tags like text * * @param string $mail the email address * @param string $title the email title. * @static * @access public * @return string */ static public function mailto($mail = '', $title = '') { $html = ''; $mails = explode(',', $mail); $titles = explode(',', $title); foreach($mails as $key => $m) { if(empty($m)) continue; $t = empty($titles[$key]) ? $mail : $titles[$key]; $html .= " $t"; } return $html; } /** * 生成select标签。 * Create tags like "" * * @param string $name the name of the select tag. * @param array $options the array to create select tag from. * @param string $selectedItems the item(s) to be selected, can like item1,item2. * @param string $attrib other params such as multiple, size and style. * @param string $append adjust if add options[$selectedItems]. * @static * @access public * @return string */ static public function select($name = '', $options = array(), $selectedItems = "", $attrib = "", $append = false) { $options = (array)($options); if($append and !isset($options[$selectedItems])) $options[$selectedItems] = $selectedItems; if(!is_array($options) or empty($options)) return false; /* The begin. */ $id = $name; if(strpos($name, '[') !== false) $id = trim(str_replace(']', '', str_replace('[', '', $name))); $id = "id='{$id}'"; if(strpos($attrib, 'id=') !== false) $id = ''; $string = "\n"; } /** * 生成带optgroup标签的select标签。 * Create select with optgroup. * * @param string $name the name of the select tag. * @param array $groups the option groups. * @param string $selectedItems the item(s) to be selected, can like item1,item2. * @param string $attrib other params such as multiple, size and style. * @static * @access public * @return string */ static public function selectGroup($name = '', $groups = array(), $selectedItems = "", $attrib = "") { if(!is_array($groups) or empty($groups)) return false; /* The begin. */ $id = $name; if(strpos($name, '[') !== false) $id = trim(str_replace(']', '', str_replace('[', '', $name))); $string = "\n"; } /** * 生成单选按钮。 * Create tags like "" * * @param string $name the name of the radio tag. * @param array $options the array to create radio tag from. * @param string $checked the value to checked by default. * @param string $attrib other attribs. * @param string $type inline or block * @static * @access public * @return string */ static public function radio($name = '', $options = array(), $checked = '', $attrib = '', $type = 'inline') { $options = (array)($options); if(!is_array($options) or empty($options)) return false; $isBlock = $type == 'block'; $string = ''; foreach($options as $key => $value) { if($isBlock) $string .= "