assign('prefix_dycode', $prefix_dycode); $this->assign('dycode', $dycode); //前端域名屏蔽 $blacklistService = new BlacklistService(); $blacklistService -> checkDomain($this->request); $time = $this->request->server('REQUEST_TIME'); UserService::instance()->getRunTimeObject()->requestTime = $time; //登录前打点用户信息 Log::info('OS:' . Ua::getOs() . ' UA:' . Ua::getUa() . ' NetType:' . Ua::getNetType() . ' IP:' . Ip::ip() . ' [' . Ip::str() . '] user_id:' . Cookie::get('user_id') . ' openid:' . Cookie::get('openid') . ' channel_id:' . Cookie::get('channel_id') . ' agent_id:' . Cookie::get('agent_id') . ' referral_id:' . Cookie::get('referral_id') . ' REMOTE_ADDR:' . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '') . ' HTTP_X_FORWARDED_FOR:' . (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '')); //渠道是否开启了广告功能 $isAdPick = model("ChannelSpecialManage")->isWhite("ad_pick", Cookie::get('channel_id')); $this->assign('isAdPick', $isAdPick); $this->assign('g_os', Ua::getOs()); $this->assign('openinstall_appkey', Config::get('site.openinstall_appkey')??''); //openinstall appkey //config('site.url_agent', 'm{0}.mp.koread.cn'); //测试用,上线删除 $this->assign('log_host', Config::get('site.loghost')); //设置打点域名 $this->assign('log_iframe_host', Config::get('site.logiframehost')??''); //设置打点域名 $this->redis = Redis::instance(); $this->assign('ad_latest_version', $this->redis->get(AdConstants::AD_LATEST_VERSION)); // 最新广告版本号 //移除HTML标签 $this->request->filter('strip_tags'); $actionUrl = UrlService::instance()->getActionUrl(); //传递APK下载地址 $appGuideInfo = AppGuideService::instance()->getAppGuideInfo(Cookie::get('channel_id')); if($appGuideInfo && !empty($appGuideInfo['app_url'])){ $apk_url = $appGuideInfo['app_url']; }else{ $apk_url = Config::get('site.default_apk_url'); } $this->assign('apk_url', $apk_url); $user_silent_data = []; // 判断是否为推广短链 if (!in_array($actionUrl, OpenPlatformConstants::$ignoreUrlArr)) { /** * 配置项处理 */ $site = Config::get("site"); $result = UrlService::instance()->checkFrontDomain(); if ($result) { if ($result->code == ErrorCodeConstants::REDIRECT) { $this->redirect($result->data); } if ($result->code == ErrorCodeConstants::EXCEPTION){ throw new HttpException(400, '异常错误,请重试'); } } if (UserService::instance()->getRunTimeObject()->urlType == OpenPlatformConstants::URL_TYPE_ADMIN) { $this->layout = 'index_default'; } $urlChannelId = UserService::instance()->getRunTimeObject()->channelId; //渠道信息 $adminConfig = AdminService::instance()->getAdminConfigModel()->getAdminInfoAll($urlChannelId); UserService::instance()->getRunTimeObject()->adminConfig = $adminConfig; /** * 浏览器环境 */ if (!Config::get('app_debug') && !Ua::isWeiXin()) { if (UserService::instance()->getRunTimeObject()->urlType == OpenPlatformConstants::URL_TYPE_PAY) { //充值域名 $this->redirect($site['scheme'] . '://' . $site['url_root']); } elseif (UserService::instance()->getRunTimeObject()->urlType == OpenPlatformConstants::URL_TYPE_SPREAD) { //推广域名 浏览器环境下显示二维码 $this->view->assign('chapter', $adminConfig); $this->view->assign('isWeChat',Ua::isWeiXin()); exit($this->view->fetch('index@layout/browser')); } } //动态初始化微信参数 OpenPlatformService::instance()->initWxParams(); //用户新建&登录 $loginResult = UserService::instance()->frontUserLogin(); if ($loginResult->code == ErrorCodeConstants::REDIRECT) { $this->assign('jumpUrl', $loginResult->data); exit($this->view->fetch('index@layout/jumpurl')); } //禁用用户,禁止访问 if (UserService::instance()->isLogin()) { $user = UserService::instance()->getUserInfo(); //更新porlardb用户拓展表ua字段ext_b: iOS=1,非iOS=2 switch (Ua::getOs()){ case 'iOS': $userUaVal = 2; break; default: $userUaVal = 1; break; } if (!Request::instance()->isAjax()) { UserVipExtendService::instance()->updateUserOs($user->id,$userUaVal); } if ((time() - (int)$user->subscribe_time) < CacheConstants::TOUTIAO_SUB_WINDOW) { //客户端缓存,支持橘子建站回传需求 $clientCache = CacheConstants::getUserClientCache($user->id); if (!Redis::instance()->exists($clientCache)) { Redis::instance()->hMSet($clientCache, ['ip' => Ip::ip(), 'ua' => $_SERVER['HTTP_USER_AGENT']]); Redis::instance()->expire($clientCache, 86400); } if (!Redis::instance()->hGet($clientCache, 'reg')) { $type = ToutiaoNotifyService::instance()::ORANGE_EVENT_TYPE_NEW; ToutiaoNotifyService::instance()->OrangeNotify($adminConfig, $user, $type); Redis::instance()->hSet($clientCache, 'reg', 1); } if ($user->is_subscribe || $user->subscription_extend) { if ($link = ToutiaoNotifyService::instance()->checkCache(Ip::ip(), $_SERVER['HTTP_USER_AGENT'] ?? '', $adminConfig['appid'])->data) { //头条回传 LogService::info('REF:'.$link); //添加支付缓存 $cachePay = CacheConstants::getGuideWxPay($user->id); $expire = 86400; Redis::instance()->set($cachePay, $link, $expire); ToutiaoNotifyService::instance()->notify($link, $user->id); } if ($link = UcNotifyService::instance()->checkCache(Ip::ip(), $_SERVER['HTTP_USER_AGENT'] ?? '', $adminConfig['appid'])->data) { //uc回传 LogService::info('UCREF:'.$link); //添加支付缓存 $cachePay = UcCacheConstants::getGuideWxPay($user->id); $expire = 86400; Redis::instance()->set($cachePay, $link, $expire); UcNotifyService::instance()->notify($user, $link); } /* //gdt follow回传 $gdtReportKey = 'GDTFR:'.$user->id; if (!Redis::instance()->exists($gdtReportKey)) { //不存在回传 LogService::info("GDTEF:u:".$user->id . ':c:'.$adminConfig['admin_id']); GdtMpApiService::instance()->follow($adminConfig, ['url' => '', 'appid' => $adminConfig['appid'], 'openid' => $user->openid]); Redis::instance()->set($gdtReportKey, $user->id, CacheConstants::TOUTIAO_SUB_WINDOW); }*/ } } //更新老用户活跃人数 if (date('Ymd') != date('Ymd', UserService::instance()->getUserInfo()->createtime)) { $channelId = AdminService::instance()->getAdminExtendModel()->getChannelId(UserService::instance()->getUserInfo()->channel_id); $activeOld = CacheConstants::getOldUserActiveCacheKey($channelId); Redis::instance()->pfAdd($activeOld, UserService::instance()->getUserInfo()->id); Redis::instance()->expire($activeOld, 86400); } // 生成微信动态码,APP使用 $prefix_dycode = ClientAppService::instance()->makeUserRegisterCode(UserService::instance()->getUserInfo()->channel_id, UserService::instance()->getUserInfo()->openid, true); $dycode = ClientAppService::instance()->makeUserRegisterCode(UserService::instance()->getUserInfo()->channel_id, UserService::instance()->getUserInfo()->openid); $this->assign('prefix_dycode', $prefix_dycode); $this->assign('dycode', $dycode); $ext = $this->request->param('ext'); //打点MQ if($ext && json_decode($ext)){ Cookie::set('ext', $ext); $update = ['ext' => $ext]; if (UrlService::instance()->checkCustomExtReferralMatch()->data && !UrlService::instance()->checkCustomExtReferralMatch($ext)->data) { $update['referral_id'] = ''; } $ext_data = json_decode($ext, true); $oAna = new AnalysisObject(); $oAna->user_from = [ "mark" => $ext_data['mark'], // 用于区分 客服消息、模板消息、书单、智能推送、自定义二维码 等 "push_id" => $ext_data['push_id'] ?? 0, "push_idx" => $ext_data['push_idx'] ?? BigData::DEFAULT_PUSH_IDX, "push_time" => $ext_data['push_time'] ?? time(), // 10位时间戳 ]; $oAna->type = [KafkaDotConstants::TYPE_VISIT]; $oAna->event_time = $time; //关注时间小于2小时对ext加关注 if ($ext_data['mark'] == MarkConstants::MARK_REPLY_PRE_RESOURCE && time() - UserService::instance()->getUserInfo()->subscribe_time < 7200 && !UserService::instance()->getUserInfo()->ext) { $oAna->type[] = KafkaDotConstants::TYPE_SUBSCRIBE; $dotData = SmartPushDotService::instance()->generateSubData( $ext_data['mark'], $ext_data['push_id'] ?? 0, $ext_data['push_time'] ?? time(), UserService::instance()->getUserInfo()->id ); SmartPushDotService::instance()->dotSmartPushInfo($dotData); } KafkaDotService::instance()->sendMsg(UserService::instance()->getUserInfo()->id, $oAna); UserService::instance()->update($update); $dotData = SmartPushDotService::instance()->generateDotData( $ext_data['mark'], $ext_data['push_id'] ?? 0, $ext_data['push_idx'] ?? BigData::DEFAULT_PUSH_IDX, $ext_data['push_time'] ?? time(), time(), array_merge(['type' => 1, 'user_id' => UserService::instance()->getUserInfo()->id], $ext_data) ); SmartPushDotService::instance()->dotSmartPushInfo($dotData); if (CustomService::instance()->checkExtIsCustom($ext)->data) { //是客服消息 UserService::instance()->setOtherDataToRedis(UserService::instance()->getUserInfo()->id, ['last_custom_id' => $ext_data['push_id']]); } } $state = UserService::instance()->getUserInfo()->state; if ($state == UserConstants::USER_STATE_DISABLE) { $this->redirect(UrlConstants::get_page_404()); } } else { if (UserService::instance()->getRunTimeObject()->urlType == OpenPlatformConstants::URL_TYPE_SPREAD) { throw new HttpException(400, '异常错误,请重试'); } } $this->assign('prefix_dycode', $prefix_dycode); $this->assign('dycode', $dycode); //region 处理静默关注逻辑 $silent_default_channel = Config::get('site.silent_default_channel'); // $silentFlag 校验渠道是否开启静默授权 $silentFlag = UserSilentService::instance()->silentChannel(Cookie::get('channel_id')); LogService::info('F:silent:静默关注逻辑:channel_id:' . Cookie::get('channel_id') . ",user_id:" . Cookie::get('user_id') . ",openid:" . Cookie::get('openid') . ',istwice:' . $this->request->param('istwice') . ',silent_default_channel:' . Config::get('site.silent_default_channel') . ',silentFlag:' . $silentFlag); $istwice = $this->request->param('istwice') ?? 0; if ( !$this->request->isAjax() && Ua::isWeiXin() && Cookie::get('channel_id') && !empty($silent_default_channel) && $silentFlag && ($istwice || ( !$istwice && $silent_default_channel != Cookie::get('channel_id') ))) { LogService::info('F:silent:静默关注逻辑:Go' ); $adminConfigInfo = model('AdminConfig')->getAdminInfoAll(Cookie::get('channel_id')); //解析请求域名 $domainParse = parse_url($this->request->domain()); // $isologous 渠道业务域名与当前请求域名是否相同(仅充值等业务,不进行该逻辑判断) $isologous = false; if ($adminConfigInfo) { $tmp_host = $adminConfigInfo['appid'] . "." . $adminConfigInfo['ophost_host']; LogService::info('F:silent:业务域名:' . $tmp_host); LogService::info('F:silent:请求域名:' . $domainParse['host']); if ($adminConfigInfo['ophost_host'] && $adminConfigInfo['appid'] && $tmp_host == $domainParse['host']) { $isologous = true; } } if ($isologous) { LogService::info('F:silent:$isologous:渠道业务域名与当前请求域名相同'); } else { LogService::info('F:silent:$isologous:渠道业务域名与当前请求域名不同'); } // 是否已经存储了关联的静默数据 TRUE | FALSE $has_user_silent = UserSilentService::instance()->getSilentData(Cookie::get('user_id'), Cookie::get('channel_id'), Cookie::get('openid'), $silent_default_channel); if ($has_user_silent) { LogService::info("F:silent:用户静默授权信息已经收集了"); } else { LogService::info("F:silent:用户静默授权信息还没有收集"); } if ( !$has_user_silent && $isologous ) { // 通过参数判断是否已经做了静默授权 $has_silent = $this->request->param('has_silent') ? 1 : 2; LogService::info("F:silent:FullUrl:" . $this->request->domain() . $this->request->url()); LogService::info("F:silent:has_silent:" . $has_silent); LogService::info("F:silent:has_silent:baseUrl:".$this->request->baseUrl()); LogService::info("F:silent:has_silent:url:".$this->request->url()); $silentHost = UserSilentService::instance()->getSlientChannelHost(); $silentUrl = 'http://'.$silentHost . $this->request->url(); $adminInfo = model('AdminConfig')->getAdminInfoAll($silent_default_channel); $wechat = new WeChatObject($adminInfo); $officialAccount = $wechat->getOfficialAccount(); if ($has_silent == 2 && $domainParse['host'] != $silentHost) { $silentParams = [ 'user_id' => UserService::instance()->getUserInfo()->id, 'channel_id' => UserService::instance()->getUserInfo()->channel_id, 'openid' => UserService::instance()->getUserInfo()->openid, 'istwice' => 1, 'jumpurl' => del_url_params(UserSilentService::instance()->refactorUrl($this->request->url(true)),'code') ]; LogService::info("F:silent:redirectSilentUrl01:" . $silentUrl); if (strpos($silentUrl, '?')) { $silentUrl .= '&' . http_build_query($silentParams); } else { $silentUrl .= '?' . http_build_query($silentParams); } //删除CODE $silentUrl = del_url_params($silentUrl, 'code'); LogService::info("F:silent:redirectSilentUrl02:" . $silentUrl); $response = $officialAccount->oauth->scopes(['snsapi_base'])->redirect($silentUrl); $this->assign('jumpUrl', $response->getTargetUrl()); LogService::info("F:silent:JumpURL:" . $response->getTargetUrl()); exit($this->view->fetch('index@layout/jumpurl_silent')); } else { LogService::info("F:silent:FullUrl2:" . $this->request->domain() . $this->request->url()); if ($this->request->has('code') && UserService::instance()->isLogin()) { LogService::info("F:silent:准备存储数据:code:" . $this->request->param('code')); /*$user = $officialAccount->oauth->user(); $tri_openid = $user->getId();*/ $tri_openid = UserService::instance()->getUserInfo()->openid; LogService::info("F:silent:saveSilentData1:" . $this->request->param('user_id') . ":" . $this->request->param('channel_id') . ":" . $this->request->param('openid')); $user_id = $this->request->param('user_id'); $channel_id = $this->request->param('channel_id'); $openid = $this->request->param('openid'); $user_silent_data = UserSilentService::instance()->saveSilentData($user_id, $channel_id, $openid, $silent_default_channel, $tri_openid); } if ($this->request->param('jumpurl')) { LogService::info("F:silent:Redirect:" . $this->request->param('jumpurl')); $this->redirect(del_url_params($this->request->param('jumpurl'), 'code')); } } } } else { LogService::info('F:silent:静默关注逻辑:NOt Go'); } //判断有没有需要转移的渠道 $isTransfer = Cookie::get('IST:' . UserService::instance()->getUserInfo()->id); if (empty($isTransfer)) { LogService::info("F:silent:isTransfer:UID:" . UserService::instance()->getUserInfo()->id . ":Cookie:" . $isTransfer); if ($user_transfer_row = Redis::instance()->hGetAll('STU:' . UserService::instance()->getUserInfo()->channel_id)) { if ($user_transfer_row['endtime'] <= time()) { $uttl = 0; } else { $uttl = $user_transfer_row['endtime'] - time(); } //判断是否转移过 $isTransfer = Redis::instance()->get('IST:' . UserService::instance()->getUserInfo()->id); if (empty($isTransfer)) { LogService::info("F:silent:isTransfer:UID:" . UserService::instance()->getUserInfo()->id . ":Redis:" . $isTransfer); $whereist['uid'] = ['eq', UserService::instance()->getUserInfo()->id]; $whereist['from_cid'] = ['eq', $user_transfer_row['send_channel_id']]; $whereist['to_cid'] = ['eq', UserService::instance()->getUserInfo()->channel_id]; $isTransferRow = model('ChannelUserTransferRecord')->where($whereist)->count(); if (!empty($isTransferRow)) { LogService::info("F:silent:isTransfer:UID:" . UserService::instance()->getUserInfo()->id . ":isTransferRow:" . json_encode($isTransferRow, JSON_UNESCAPED_UNICODE)); $isTransfer = 1; Redis::instance()->setex('IST:' . UserService::instance()->getUserInfo()->id, $uttl, 1);//ttl:5d Cookie::set('IST:' . UserService::instance()->getUserInfo()->id, 1, $uttl);//ttl:5d } } if (empty($isTransfer)) { //去转移 $transfered = UserSilentService::instance()->channelFansTransfer( $user_silent_data, UserService::instance()->getUserInfo()->id, UserService::instance()->getUserInfo()->channel_id, UserService::instance()->getUserInfo()->openid, $user_transfer_row['send_channel_id'] ); if ($transfered) { LogService::info("F:silent:isTransfer:UID:" . UserService::instance()->getUserInfo()->id . ":transfered:" . $transfered); Redis::instance()->setex('IST:' . UserService::instance()->getUserInfo()->id, $uttl, 1);//ttl:5d Cookie::set('IST:' . UserService::instance()->getUserInfo()->id, 1, $uttl);//ttl:5d } } } } //endregion $this->assign('user_id', UserService::instance()->getUserInfo()->id); //处理推广链接 $referral_result = UrlService::instance()->processReferral(); if ($referral_result->code == ErrorCodeConstants::REDIRECT) { $this->redirect($referral_result->data); } $moduleName = Request::instance()->module(); $controllerName = strtolower(Request::instance()->controller()); $actionName = strtolower(Request::instance()->action()); // 配置信息 $config = [ 'site' => array_intersect_key($site, array_flip(['name', 'cdnurl', 'version', 'timezone', 'languages'])), 'modulename' => $moduleName, 'controllername' => $controllerName, 'actionname' => $actionName, 'jsname' => 'frontend/' . str_replace('.', '/', $controllerName), 'moduleurl' => rtrim(url("/{$moduleName}", '', false), '/'), ]; // 配置信息后 Hook::listen("config_init", $config); $this->assign('site', $site); $this->assign('config', $config); $isAddBackDom = 0; if (in_array( $actionUrl, [ 'index/user/recent', 'index/book/info', 'index/book/chapter', 'index/index/index' ] ) ) { $isAddBackDom = 1; } $this->assign('isAddBackDom', $isAddBackDom); /** * 如果有使用模板布局 */ if ($this->layout) { $this->view->engine->layout('layout/' . $this->layout); } //设置参数 $this->user = UserService::instance(); $this->adminconfig = UserService::instance()->getRunTimeObject()->adminConfig; $this->urlType = UserService::instance()->getRunTimeObject()->urlType; $this->urlAgentId = UserService::instance()->getRunTimeObject()->agentId; $this->urlAdminId = UserService::instance()->getRunTimeObject()->adminId; $this->urlChannelId = UserService::instance()->getRunTimeObject()->channelId; $this->appid = UserService::instance()->getRunTimeObject()->appId; $this->time = UserService::instance()->getRunTimeObject()->requestTime; } } /** * 获取URL参数 * @param $query * @return array */ function getUrlParams($query) { $queryParts = explode('&', $query); $params = []; if ($queryParts) { foreach ($queryParts as $param) { $item = explode('=', $param); $params[$item[0]] = $item[1]; } } return $params; } /** * 删除URL指定参数 * @param $paramNames * @param $url * @return string */ protected function removeUrlParam($paramNames, $url) { if (is_string($paramNames)) { $paramNames = [$paramNames]; } $baseUrl = $url; $arr = []; if (strpos($url, "?") !== false) { $urlInfo = explode("?", $url); $baseUrl = $urlInfo[1]; parse_str($baseUrl, $arr); } foreach ($arr as $name => $v) { if (in_array($name, $paramNames)) { unset($arr[$name]); } } return $urlInfo[0] . "?" . http_build_query($arr); } /** * 加载语言文件 * @param string $name */ protected function loadlang($name) { Lang::load(APP_PATH . $this->request->module() . '/lang/' . Lang::detect() . '/' . str_replace('.', '/', $name) . '.php'); } /** * 渲染配置信息 * @param mixed $name 键名或数组 * @param mixed $value 值 */ protected function assignconfig($name, $value = '') { $this->view->config = array_merge($this->view->config ? $this->view->config : [], is_array($name) ? $name : [$name => $value]); } /** * 关注回复资源继续阅读 * @param $responseModel * @param $adminConfig * @return array */ protected function getContinueTipData($responseModel, $adminConfig) { $push_time = time(); $replace = []; $appid = $adminConfig['appid']; $content = UrlService::instance()->replaceReferralHost( $adminConfig['admin_id'], $responseModel->content, false )->data; switch ($responseModel->type) { case "text": $mark = MarkConstants::MARK_RESOURCE; preg_match_all("", $content, $match); if ($match) { foreach ($match[1] as $index => $item) { $push_idx = $index + 1; $parse = parse_url($item); $host = $parse['host'] ?? ''; $path = $parse['path'] ?? ''; if (preg_match('/^' . $appid . '/', $host)) { if (array_key_exists('query', $parse)) { $query = parse_query($parse['query']); if (!empty($query)) { if (array_key_exists('book_id', $query)) { $push_id = $query['book_id']; $replace = [ 'book_id' => $query['book_id'], 'url' => $item .= '&' . OfficialAccountsEventService::instance()->processExt($mark, $push_id, $push_idx, $push_time), ]; break; } elseif (array_key_exists('t', $query)) { $push_id = $query['t']; $replace = [ 'book_id' => '', 'url' => $item .= '&' . OfficialAccountsEventService::instance()->processExt($mark, $push_id, $push_idx, $push_time), 'referral_id' => $query['t'], ]; break; } } } if (!empty($path)) { $path = explode('/', $parse['path']); if ($keyIndex = array_search('t', $path)) { $push_id = $path[$keyIndex+1]; $replace = [ 'book_id' => '', 'url' => $item .= '?' . OfficialAccountsEventService::instance()->processExt($mark, $push_id, $push_idx, $push_time), 'referral_id' => $push_id, ]; break; } } } } } break; case "news": $mark = MarkConstants::MARK_RESOURCE; $items = json_decode($content, true); foreach ($items as $index => $item) { $push_idx = $index + 1; $parse = parse_url($item['url']); $path = $parse['path'] ?? ''; if (array_key_exists('query', $parse)) { $query = parse_query($parse['query']); if (array_key_exists('book_id', $query)) { $push_id = $query['book_id']; $replace = [ 'book_id' => $query['book_id'], 'url' => $item['url'] .= '&' . OfficialAccountsEventService::instance()->processExt($mark, $push_id, $push_idx, $push_time), 'referral_id' => '' ]; break; } elseif (array_key_exists('t', $query)) { $push_id = $query['t']; $replace = [ 'book_id' => '', 'url' => $item['url'] .= '&' . OfficialAccountsEventService::instance()->processExt($mark, $push_id, $push_idx, $push_time), 'referral_id' => $query['t'], ]; break; } else { continue; } } if (!empty($path)) { $path = explode('/', $parse['path']); if ($keyIndex = array_search('t', $path)) { $push_id = $path[$keyIndex+1]; $replace = [ 'book_id' => '', 'url' => $item['url'] .= '?' . OfficialAccountsEventService::instance()->processExt($mark, $push_id, $push_idx, $push_time), 'referral_id' => $push_id, ]; break; } } } break; } if (!empty($replace)) { //查询小说标题 if (isset($replace['referral_id']) && $replace['referral_id']) { $referralinfo = model('referral')->getone($replace['referral_id'], false); if (empty($referralinfo) || !$referralinfo['book_id']) { return []; } $replace['book_id'] = $referralinfo['book_id']; } $bookinfo = BookService::instance()->getBookModel()->BookInfo($replace['book_id']); $replace['book_name'] = $bookinfo['name']; $replace['image'] = $bookinfo['image']; $replace['description'] = $bookinfo['description']; } return $replace; } }