Merge branch 'develop' of https://gitee.com/Tyssen/yi-shi into develop
This commit is contained in:
@@ -204,4 +204,39 @@ const mapRestrictionType = (type: string): "friend_limit" | "marketing" | "spam"
|
||||
};
|
||||
|
||||
return typeMap[type] || 'other';
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取微信好友列表
|
||||
* @param wechatId 微信账号ID
|
||||
* @param page 页码
|
||||
* @param limit 每页数量
|
||||
* @param keyword 搜索关键词
|
||||
* @returns 好友列表数据
|
||||
*/
|
||||
export const fetchWechatFriends = async (
|
||||
wechatId: string | number,
|
||||
page: number = 1,
|
||||
limit: number = 20,
|
||||
keyword: string = ""
|
||||
): Promise<{ code: number; msg: string; data: any }> => {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
wechatId: String(wechatId),
|
||||
page: String(page),
|
||||
limit: String(limit)
|
||||
});
|
||||
|
||||
if (keyword) {
|
||||
params.append('keyword', keyword);
|
||||
}
|
||||
|
||||
const url = `/v1/device/wechats/friends?${params.toString()}`;
|
||||
const response = await api.get<{ code: number; msg: string; data: any }>(url);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('获取微信好友列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
interface WechatAccount {
|
||||
wechatId: string
|
||||
@@ -169,7 +170,10 @@ export function DeviceSelector({
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">IMEI: {device.imei}</div>
|
||||
<div className="text-sm text-gray-500 flex items-center">
|
||||
<span className="mr-1">IMEI:</span>
|
||||
<ImeiDisplay imei={device.imei} containerWidth={160} />
|
||||
</div>
|
||||
<div className="mt-2 space-y-2">
|
||||
{device.wechatAccounts.map((account) => (
|
||||
<div key={account.wechatId} className="bg-gray-50 rounded-lg p-2">
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||
import { Battery, Smartphone, MessageCircle, Users, Clock } from "lucide-react"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
export interface Device {
|
||||
id: string
|
||||
@@ -88,11 +89,15 @@ export function DeviceGrid({
|
||||
/>
|
||||
)}
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="font-medium">{device.name}</div>
|
||||
<Badge variant={device.status === "online" ? "success" : "secondary"}>
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
<div className="relative">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="font-medium">{device.name}</div>
|
||||
<div className="absolute top-2 right-2">
|
||||
<Badge variant={device.status === "online" ? "default" : "secondary"}>
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 text-sm text-gray-500">
|
||||
@@ -142,10 +147,13 @@ export function DeviceGrid({
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium">{selectedDevice.name}</h3>
|
||||
<p className="text-sm text-gray-500">IMEI: {selectedDevice.imei}</p>
|
||||
<p className="text-sm text-gray-500 flex items-center">
|
||||
<span className="mr-1">IMEI:</span>
|
||||
<ImeiDisplay imei={selectedDevice.imei} containerWidth={160} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant={selectedDevice.status === "online" ? "success" : "secondary"}>
|
||||
<Badge variant={selectedDevice.status === "online" ? "default" : "secondary"}>
|
||||
{selectedDevice.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Card } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
interface WechatAccount {
|
||||
wechatId: string
|
||||
@@ -278,7 +279,10 @@ export function DeviceSelectionDialog({
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">IMEI: {device.imei}</div>
|
||||
<div className="text-sm text-gray-500 flex items-center">
|
||||
<span className="mr-1">IMEI:</span>
|
||||
<ImeiDisplay imei={device.imei} containerWidth={160} />
|
||||
</div>
|
||||
|
||||
{/* 微信账号信息 */}
|
||||
<div className="mt-2 space-y-2">
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Label } from "@/components/ui/label"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { fetchDeviceDetail, fetchDeviceRelatedAccounts, updateDeviceTaskConfig, fetchDeviceHandleLogs } from "@/api/devices"
|
||||
import { toast } from "sonner"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
interface WechatAccount {
|
||||
id: string
|
||||
@@ -439,7 +440,10 @@ export default function DeviceDetailPage() {
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 mt-1">IMEI: {device.imei}</div>
|
||||
<div className="text-sm text-gray-500 mt-1 flex items-center imei-display-area">
|
||||
<span className="mr-1 whitespace-nowrap">IMEI:</span>
|
||||
<ImeiDisplay imei={device.imei} containerWidth="max-w-[calc(100%-60px)]" />
|
||||
</div>
|
||||
{device.historicalIds && device.historicalIds.length > 0 && (
|
||||
<div className="text-sm text-gray-500">历史ID: {device.historicalIds.join(", ")}</div>
|
||||
)}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { fetchDeviceList, deleteDevice } from "@/api/devices"
|
||||
import { ServerDevice } from "@/types/device"
|
||||
import { api } from "@/lib/api"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
// 设备接口更新为与服务端接口对应的类型
|
||||
interface Device extends ServerDevice {
|
||||
@@ -479,8 +480,23 @@ export default function DevicesPage() {
|
||||
}
|
||||
|
||||
// 设备详情页跳转
|
||||
const handleDeviceClick = (deviceId: number) => {
|
||||
router.push(`/devices/${deviceId}`)
|
||||
const handleDeviceClick = (deviceId: number, event: React.MouseEvent) => {
|
||||
// 判断点击事件是否来自ImeiDisplay组件或其后代元素
|
||||
// 如果点击事件已经被处理(例如ImeiDisplay中已阻止传播),则不执行跳转
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果点击的元素或其父元素有imei-display类,则不跳转
|
||||
let target = event.target as HTMLElement;
|
||||
while (target && target !== event.currentTarget) {
|
||||
if (target.classList.contains('imei-display-area')) {
|
||||
return;
|
||||
}
|
||||
target = target.parentElement as HTMLElement;
|
||||
}
|
||||
|
||||
router.push(`/devices/${deviceId}`);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -573,7 +589,7 @@ export default function DevicesPage() {
|
||||
<Card
|
||||
key={device.id}
|
||||
className="p-3 hover:shadow-md transition-shadow cursor-pointer relative"
|
||||
onClick={() => handleDeviceClick(device.id)}
|
||||
onClick={(e) => handleDeviceClick(device.id, e)}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Checkbox
|
||||
@@ -594,7 +610,10 @@ export default function DevicesPage() {
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">IMEI: {device.imei}</div>
|
||||
<div className="text-sm text-gray-500 flex items-center imei-display-area">
|
||||
<span className="mr-1">IMEI:</span>
|
||||
<ImeiDisplay imei={device.imei} containerWidth={180} />
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">微信号: {device.wechatId || "未绑定"}</div>
|
||||
<div className="flex items-center justify-between mt-1 text-sm">
|
||||
<span className="text-gray-500">好友数: {device.totalFriend}</span>
|
||||
|
||||
@@ -58,3 +58,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 修复微信号页面横向滚动问题 */
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 优化微信账号列表项布局 */
|
||||
.Card[class*="wechat-accounts"] {
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 确保进度条组件不会溢出 */
|
||||
.Progress {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 修复IMEI显示模态框点击事件问题 */
|
||||
.imei-display {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.imei-display-area {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* 增强模态框的点击隔离 */
|
||||
[role="dialog"] {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useState, useEffect, useRef, useCallback } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { Card } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
import { fetchWechatAccountDetail, transformWechatAccountDetail } from "@/api/wechat-accounts"
|
||||
import { fetchWechatAccountDetail, transformWechatAccountDetail, fetchWechatFriends } from "@/api/wechat-accounts"
|
||||
|
||||
interface RestrictionRecord {
|
||||
id: string
|
||||
@@ -120,126 +120,34 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
const [showFriendDetail, setShowFriendDetail] = useState(false)
|
||||
const [selectedFriend, setSelectedFriend] = useState<WechatFriend | null>(null)
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [activeTab, setActiveTab] = useState("overview")
|
||||
const friendsPerPage = 10
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
// 模拟API调用获取账号详情
|
||||
const fetchAccount = async () => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
|
||||
// 调用API获取微信账号详情
|
||||
const response = await fetchWechatAccountDetail(params.id)
|
||||
|
||||
if (response && response.code === 200) {
|
||||
// 转换数据格式
|
||||
const transformedAccount = transformWechatAccountDetail(response)
|
||||
setAccount(transformedAccount)
|
||||
} else {
|
||||
toast({
|
||||
title: "获取微信账号详情失败",
|
||||
description: response?.msg || "请稍后再试",
|
||||
variant: "destructive"
|
||||
})
|
||||
// 获取失败时使用模拟数据
|
||||
setAccount(generateMockAccountData())
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取微信账号详情失败:", error)
|
||||
toast({
|
||||
title: "获取微信账号详情失败",
|
||||
description: "请检查网络连接或稍后再试",
|
||||
variant: "destructive"
|
||||
})
|
||||
// 请求出错时使用模拟数据
|
||||
setAccount(generateMockAccountData())
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
// 好友列表相关状态
|
||||
const [friends, setFriends] = useState<any[]>([])
|
||||
const [friendsPage, setFriendsPage] = useState(1)
|
||||
const [friendsTotal, setFriendsTotal] = useState(0)
|
||||
const [hasMoreFriends, setHasMoreFriends] = useState(true)
|
||||
const [isFetchingFriends, setIsFetchingFriends] = useState(false)
|
||||
const [hasFriendLoadError, setHasFriendLoadError] = useState(false)
|
||||
const friendsObserver = useRef<IntersectionObserver | null>(null)
|
||||
const friendsLoadingRef = useRef<HTMLDivElement | null>(null)
|
||||
const friendsContainerRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
fetchAccount()
|
||||
}, [params.id])
|
||||
|
||||
if (!account) {
|
||||
return <div>加载中...</div>
|
||||
}
|
||||
|
||||
const getWeightColor = (weight: number) => {
|
||||
if (weight >= 80) return "text-green-600"
|
||||
if (weight >= 60) return "text-yellow-600"
|
||||
return "text-red-600"
|
||||
}
|
||||
|
||||
const getWeightDescription = (weight: number) => {
|
||||
if (weight >= 80) return "账号状态良好"
|
||||
if (weight >= 60) return "账号状态一般"
|
||||
return "账号状态较差"
|
||||
}
|
||||
|
||||
const calculateMaxDailyAdds = (weight: number) => {
|
||||
const baseLimit = 20
|
||||
return Math.floor(baseLimit * (weight / 100))
|
||||
}
|
||||
|
||||
const getRestrictionTypeColor = (type: string) => {
|
||||
switch (type) {
|
||||
case "friend_limit":
|
||||
return "text-yellow-600"
|
||||
case "marketing":
|
||||
return "text-red-600"
|
||||
case "spam":
|
||||
return "text-orange-600"
|
||||
default:
|
||||
return "text-gray-600"
|
||||
}
|
||||
}
|
||||
|
||||
const formatAccountAge = (age: { years: number; months: number }) => {
|
||||
if (age.years === 0) {
|
||||
return `${age.months}个月`
|
||||
}
|
||||
if (age.months === 0) {
|
||||
return `${age.years}年`
|
||||
}
|
||||
return `${age.years}年${age.months}个月`
|
||||
}
|
||||
|
||||
const handleTransferFriends = () => {
|
||||
setShowTransferConfirm(true)
|
||||
}
|
||||
|
||||
const confirmTransferFriends = () => {
|
||||
setShowTransferConfirm(false)
|
||||
// 跳转到新建计划的订单导入场景
|
||||
router.push(`/scenarios/new?type=order&source=${account.wechatId}`)
|
||||
}
|
||||
|
||||
const handleFriendClick = (friend: WechatFriend) => {
|
||||
setSelectedFriend(friend)
|
||||
setShowFriendDetail(true)
|
||||
}
|
||||
|
||||
// 过滤好友
|
||||
const filteredFriends = account.friends.filter(
|
||||
(friend) =>
|
||||
friend.nickname.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
friend.wechatId.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
friend.remark.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
friend.tags.some((tag) => tag.name.toLowerCase().includes(searchQuery.toLowerCase())),
|
||||
)
|
||||
|
||||
// 分页
|
||||
const totalPages = Math.ceil(filteredFriends.length / friendsPerPage)
|
||||
const paginatedFriends = filteredFriends.slice((currentPage - 1) * friendsPerPage, currentPage * friendsPerPage)
|
||||
// 计算好友列表容器高度
|
||||
const getFriendsContainerHeight = () => {
|
||||
// 最少显示一条记录的高度,最多显示十条记录的高度
|
||||
const minHeight = 80; // 单条记录高度
|
||||
const maxHeight = 800; // 十条记录高度
|
||||
|
||||
if (friends.length === 0) return minHeight;
|
||||
return Math.min(Math.max(friends.length * 80, minHeight), maxHeight);
|
||||
};
|
||||
|
||||
// 生成模拟账号数据(作为备用,服务器请求失败时使用)
|
||||
const generateMockAccountData = () => {
|
||||
const generateMockAccountData = (): WechatAccountDetail => {
|
||||
// 生成随机标签
|
||||
const generateRandomTags = (count: number) => {
|
||||
const generateRandomTags = (count: number): FriendTag[] => {
|
||||
const tagPool = [
|
||||
{ name: "潜在客户", color: "bg-blue-100 text-blue-800" },
|
||||
{ name: "高意向", color: "bg-green-100 text-green-800" },
|
||||
@@ -251,36 +159,36 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
{ name: "个人用户", color: "bg-pink-100 text-pink-800" },
|
||||
{ name: "新增好友", color: "bg-emerald-100 text-emerald-800" },
|
||||
{ name: "老客户", color: "bg-amber-100 text-amber-800" },
|
||||
]
|
||||
];
|
||||
|
||||
return Array.from({ length: Math.floor(Math.random() * count) + 1 }, () => {
|
||||
const randomTag = tagPool[Math.floor(Math.random() * tagPool.length)]
|
||||
const randomTag = tagPool[Math.floor(Math.random() * tagPool.length)];
|
||||
return {
|
||||
id: `tag-${Math.random().toString(36).substring(2, 9)}`,
|
||||
name: randomTag.name,
|
||||
color: randomTag.color,
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 生成随机好友
|
||||
const friendCount = Math.floor(Math.random() * (300 - 150)) + 150
|
||||
const generateFriends = (count: number) => {
|
||||
const friendCount = Math.floor(Math.random() * (300 - 150)) + 150;
|
||||
const generateFriends = (count: number): WechatFriend[] => {
|
||||
return Array.from({ length: count }, (_, i) => {
|
||||
const firstName = ["张", "王", "李", "赵", "陈", "刘", "杨", "黄", "周", "吴"][Math.floor(Math.random() * 10)]
|
||||
const firstName = ["张", "王", "李", "赵", "陈", "刘", "杨", "黄", "周", "吴"][Math.floor(Math.random() * 10)];
|
||||
const secondName = ["小", "大", "明", "华", "强", "伟", "芳", "娜", "秀", "英"][
|
||||
Math.floor(Math.random() * 10)
|
||||
]
|
||||
const lastName = ["明", "华", "强", "伟", "芳", "娜", "秀", "英", "军", "杰"][Math.floor(Math.random() * 10)]
|
||||
const nickname = firstName + secondName + lastName
|
||||
];
|
||||
const lastName = ["明", "华", "强", "伟", "芳", "娜", "秀", "英", "军", "杰"][Math.floor(Math.random() * 10)];
|
||||
const nickname = firstName + secondName + lastName;
|
||||
|
||||
// 生成随机的添加时间(过去1年内)
|
||||
const addDate = new Date()
|
||||
addDate.setDate(addDate.getDate() - Math.floor(Math.random() * 365))
|
||||
const addDate = new Date();
|
||||
addDate.setDate(addDate.getDate() - Math.floor(Math.random() * 365));
|
||||
|
||||
// 生成随机的最后互动时间(过去30天内)
|
||||
const lastDate = new Date()
|
||||
lastDate.setDate(lastDate.getDate() - Math.floor(Math.random() * 30))
|
||||
const lastDate = new Date();
|
||||
lastDate.setDate(lastDate.getDate() - Math.floor(Math.random() * 30));
|
||||
|
||||
return {
|
||||
id: `friend-${i}`,
|
||||
@@ -306,11 +214,11 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
Math.floor(Math.random() * 5)
|
||||
]
|
||||
: "",
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const friends = generateFriends(friendCount)
|
||||
const friends = generateFriends(friendCount);
|
||||
|
||||
const mockAccount: WechatAccountDetail = {
|
||||
id: params.id,
|
||||
@@ -363,8 +271,241 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
messages: Math.floor(Math.random() * 100) + 100,
|
||||
})),
|
||||
friends: friends,
|
||||
};
|
||||
return mockAccount;
|
||||
};
|
||||
|
||||
// 随机生成标签颜色
|
||||
const getRandomTagColor = (): string => {
|
||||
const colors = [
|
||||
"bg-blue-100 text-blue-800",
|
||||
"bg-green-100 text-green-800",
|
||||
"bg-red-100 text-red-800",
|
||||
"bg-pink-100 text-pink-800",
|
||||
"bg-emerald-100 text-emerald-800",
|
||||
"bg-amber-100 text-amber-800",
|
||||
];
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
};
|
||||
|
||||
// 获取好友列表数据
|
||||
const fetchFriends = useCallback(async (page: number = 1, isNewSearch: boolean = false) => {
|
||||
if (!account || isFetchingFriends) return;
|
||||
|
||||
try {
|
||||
setIsFetchingFriends(true);
|
||||
setHasFriendLoadError(false);
|
||||
|
||||
// 调用API获取好友列表
|
||||
const response = await fetchWechatFriends(account.wechatId, page, 20, searchQuery);
|
||||
|
||||
if (response && response.code === 200) {
|
||||
// 更新总数计数,确保在第一次加载时设置
|
||||
if (isNewSearch || friendsTotal === 0) {
|
||||
setFriendsTotal(response.data.total || 0);
|
||||
}
|
||||
|
||||
const newFriends = response.data.list.map((friend: any) => ({
|
||||
id: friend.wechatId,
|
||||
avatar: friend.avatar,
|
||||
nickname: friend.nickname || '未设置昵称',
|
||||
wechatId: friend.wechatId,
|
||||
remark: friend.remark || '',
|
||||
addTime: '2024-01-01', // 接口未返回,使用默认值
|
||||
lastInteraction: '2024-01-01', // 接口未返回,使用默认值
|
||||
tags: (friend.labels || []).map((label: string, index: number) => ({
|
||||
id: `tag-${index}`,
|
||||
name: label,
|
||||
color: getRandomTagColor(),
|
||||
})),
|
||||
region: friend.region || '未知地区',
|
||||
source: '微信好友', // 接口未返回,使用默认值
|
||||
notes: '',
|
||||
}));
|
||||
|
||||
// 更新状态
|
||||
if (isNewSearch) {
|
||||
setFriends(newFriends);
|
||||
} else {
|
||||
setFriends(prev => [...prev, ...newFriends]);
|
||||
}
|
||||
|
||||
setFriendsPage(page);
|
||||
// 判断是否还有更多数据
|
||||
setHasMoreFriends(page * 20 < response.data.total);
|
||||
|
||||
console.log("好友列表加载成功,总数:", response.data.total);
|
||||
} else {
|
||||
setHasFriendLoadError(true);
|
||||
toast({
|
||||
title: "获取好友列表失败",
|
||||
description: response?.msg || "请稍后再试",
|
||||
variant: "destructive"
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setHasFriendLoadError(true);
|
||||
console.error("获取好友列表失败:", error);
|
||||
toast({
|
||||
title: "获取好友列表失败",
|
||||
description: "请检查网络连接或稍后再试",
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setIsFetchingFriends(false);
|
||||
}
|
||||
return mockAccount
|
||||
}, [account, searchQuery, friendsTotal]);
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = useCallback(() => {
|
||||
setFriends([]);
|
||||
setFriendsPage(1);
|
||||
setHasMoreFriends(true);
|
||||
fetchFriends(1, true);
|
||||
}, [fetchFriends]);
|
||||
|
||||
// 处理标签切换
|
||||
useEffect(() => {
|
||||
if (account && friends.length === 0) {
|
||||
fetchFriends(1, true);
|
||||
}
|
||||
}, [account, friends.length, fetchFriends]);
|
||||
|
||||
// 设置IntersectionObserver用于懒加载
|
||||
useEffect(() => {
|
||||
friendsObserver.current = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && hasMoreFriends && !isFetchingFriends) {
|
||||
fetchFriends(friendsPage + 1);
|
||||
}
|
||||
}, { threshold: 0.5 });
|
||||
|
||||
return () => {
|
||||
if (friendsObserver.current) {
|
||||
friendsObserver.current.disconnect();
|
||||
}
|
||||
};
|
||||
}, [fetchFriends, friendsPage, hasMoreFriends, isFetchingFriends]);
|
||||
|
||||
// 观察加载指示器
|
||||
useEffect(() => {
|
||||
if (friendsLoadingRef.current && friendsObserver.current) {
|
||||
friendsObserver.current.observe(friendsLoadingRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (friendsLoadingRef.current && friendsObserver.current) {
|
||||
friendsObserver.current.unobserve(friendsLoadingRef.current);
|
||||
}
|
||||
};
|
||||
}, [friendsLoadingRef.current, friendsObserver.current]);
|
||||
|
||||
useEffect(() => {
|
||||
// 模拟API调用获取账号详情
|
||||
const fetchAccount = async () => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
|
||||
// 调用API获取微信账号详情
|
||||
const response = await fetchWechatAccountDetail(params.id)
|
||||
|
||||
if (response && response.code === 200) {
|
||||
// 转换数据格式
|
||||
const transformedAccount = transformWechatAccountDetail(response)
|
||||
setAccount(transformedAccount)
|
||||
|
||||
// 如果有好友总数,更新friendsTotal状态
|
||||
if (transformedAccount && transformedAccount.friendCount > 0) {
|
||||
setFriendsTotal(transformedAccount.friendCount);
|
||||
}
|
||||
} else {
|
||||
toast({
|
||||
title: "获取微信账号详情失败",
|
||||
description: response?.msg || "请稍后再试",
|
||||
variant: "destructive"
|
||||
})
|
||||
// 获取失败时使用模拟数据
|
||||
const mockData = generateMockAccountData();
|
||||
setAccount(mockData);
|
||||
// 更新好友总数
|
||||
setFriendsTotal(mockData.friendCount);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取微信账号详情失败:", error)
|
||||
toast({
|
||||
title: "获取微信账号详情失败",
|
||||
description: "请检查网络连接或稍后再试",
|
||||
variant: "destructive"
|
||||
})
|
||||
// 请求出错时使用模拟数据
|
||||
const mockData = generateMockAccountData();
|
||||
setAccount(mockData);
|
||||
// 更新好友总数
|
||||
setFriendsTotal(mockData.friendCount);
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchAccount()
|
||||
}, [params.id])
|
||||
|
||||
if (!account) {
|
||||
return <div>加载中...</div>
|
||||
}
|
||||
|
||||
const getWeightColor = (weight: number) => {
|
||||
if (weight >= 80) return "text-green-600"
|
||||
if (weight >= 60) return "text-yellow-600"
|
||||
return "text-red-600"
|
||||
}
|
||||
|
||||
const getWeightDescription = (weight: number) => {
|
||||
if (weight >= 80) return "账号状态良好"
|
||||
if (weight >= 60) return "账号状态一般"
|
||||
return "账号状态较差"
|
||||
}
|
||||
|
||||
const calculateMaxDailyAdds = (weight: number) => {
|
||||
const baseLimit = 20
|
||||
return Math.floor(baseLimit * (weight / 100))
|
||||
}
|
||||
|
||||
const getRestrictionTypeColor = (type: string) => {
|
||||
switch (type) {
|
||||
case "friend_limit":
|
||||
return "text-yellow-600"
|
||||
case "marketing":
|
||||
return "text-red-600"
|
||||
case "spam":
|
||||
return "text-orange-600"
|
||||
default:
|
||||
return "text-gray-600"
|
||||
}
|
||||
}
|
||||
|
||||
const formatAccountAge = (age: { years: number; months: number }) => {
|
||||
if (age.years > 0) {
|
||||
return `${age.years}年${age.months}个月`;
|
||||
}
|
||||
return `${age.months}个月`;
|
||||
};
|
||||
|
||||
const handleTransferFriends = () => {
|
||||
setShowTransferConfirm(true)
|
||||
}
|
||||
|
||||
const confirmTransferFriends = () => {
|
||||
// 模拟API调用
|
||||
toast({
|
||||
title: "好友转移成功",
|
||||
description: `已成功转移 ${account?.friends.length} 个好友`,
|
||||
});
|
||||
setShowTransferConfirm(false)
|
||||
}
|
||||
|
||||
const handleFriendClick = (friend: WechatFriend) => {
|
||||
setSelectedFriend(friend)
|
||||
setShowFriendDetail(true)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -374,7 +515,7 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
<Loader2 className="h-8 w-8 animate-spin text-blue-500" />
|
||||
</div>
|
||||
) : account ? (
|
||||
<div className="flex-1 bg-gradient-to-b from-blue-50 to-white min-h-screen pb-16">
|
||||
<div className="flex-1 bg-gradient-to-b from-blue-50 to-white min-h-screen pb-16 overflow-x-hidden">
|
||||
<header className="sticky top-0 z-10 bg-white/80 backdrop-blur-sm border-b">
|
||||
<div className="flex items-center p-4">
|
||||
<Button variant="ghost" size="icon" onClick={() => router.back()}>
|
||||
@@ -400,7 +541,7 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<h2 className="text-xl font-semibold">{account.nickname}</h2>
|
||||
<h2 className="text-xl font-semibold truncate max-w-[200px]">{account.nickname}</h2>
|
||||
<Badge variant={account.status === "normal" ? "outline" : "destructive"}>
|
||||
{account.status === "normal" ? "正常" : "异常"}
|
||||
</Badge>
|
||||
@@ -423,7 +564,9 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="overview">账号概览</TabsTrigger>
|
||||
<TabsTrigger value="friends">好友列表 ({account.friendCount})</TabsTrigger>
|
||||
<TabsTrigger value="friends">
|
||||
好友列表 ({friendsTotal > 0 ? friendsTotal : account.friendCount})
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="overview" className="space-y-4 mt-4">
|
||||
@@ -463,24 +606,24 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
<p className="text-sm text-gray-500 mb-4">{getWeightDescription(account.accountWeight)}</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span>账号年龄</span>
|
||||
<Progress value={account.weightFactors.ageFactor * 100} className="w-32" />
|
||||
<span>{(account.weightFactors.ageFactor * 100).toFixed(0)}%</span>
|
||||
<span className="flex-shrink-0">账号年龄</span>
|
||||
<Progress value={account.weightFactors.ageFactor * 100} className="flex-1 min-w-0 mx-2" />
|
||||
<span className="flex-shrink-0">{(account.weightFactors.ageFactor * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span>活跃度</span>
|
||||
<Progress value={account.weightFactors.activityFactor * 100} className="w-32" />
|
||||
<span>{(account.weightFactors.activityFactor * 100).toFixed(0)}%</span>
|
||||
<span className="flex-shrink-0">活跃度</span>
|
||||
<Progress value={account.weightFactors.activityFactor * 100} className="flex-1 min-w-0 mx-2" />
|
||||
<span className="flex-shrink-0">{(account.weightFactors.activityFactor * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span>限制影响</span>
|
||||
<Progress value={account.weightFactors.restrictionFactor * 100} className="w-32" />
|
||||
<span>{(account.weightFactors.restrictionFactor * 100).toFixed(0)}%</span>
|
||||
<span className="flex-shrink-0">限制影响</span>
|
||||
<Progress value={account.weightFactors.restrictionFactor * 100} className="flex-1 min-w-0 mx-2" />
|
||||
<span className="flex-shrink-0">{(account.weightFactors.restrictionFactor * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span>实名认证</span>
|
||||
<Progress value={account.weightFactors.verificationFactor * 100} className="w-32" />
|
||||
<span>{(account.weightFactors.verificationFactor * 100).toFixed(0)}%</span>
|
||||
<span className="flex-shrink-0">实名认证</span>
|
||||
<Progress value={account.weightFactors.verificationFactor * 100} className="flex-1 min-w-0 mx-2" />
|
||||
<span className="flex-shrink-0">{(account.weightFactors.verificationFactor * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -561,107 +704,97 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
placeholder="搜索好友昵称/微信号/备注/标签"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
className="pl-9"
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" size="icon">
|
||||
<Button variant="outline" size="icon" onClick={handleSearch}>
|
||||
<Filter className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 好友列表 */}
|
||||
<div className="space-y-2">
|
||||
{paginatedFriends.length === 0 ? (
|
||||
<div
|
||||
ref={friendsContainerRef}
|
||||
className="space-y-2 transition-all duration-300"
|
||||
style={{
|
||||
minHeight: '80px',
|
||||
height: `${getFriendsContainerHeight()}px`,
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
>
|
||||
{isFetchingFriends && friends.length === 0 ? (
|
||||
<div className="flex justify-center items-center py-8">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-blue-500" />
|
||||
</div>
|
||||
) : friends.length === 0 && hasFriendLoadError ? (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
<p>加载好友失败,请重试</p>
|
||||
<Button variant="outline" size="sm" className="mt-2" onClick={() => fetchFriends(1, true)}>
|
||||
重新加载
|
||||
</Button>
|
||||
</div>
|
||||
) : friends.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-500">未找到匹配的好友</div>
|
||||
) : (
|
||||
paginatedFriends.map((friend) => (
|
||||
<div
|
||||
key={friend.id}
|
||||
className="flex items-center p-3 border rounded-lg hover:bg-gray-50 cursor-pointer"
|
||||
onClick={() => handleFriendClick(friend)}
|
||||
>
|
||||
<Avatar className="h-10 w-10 mr-3">
|
||||
<AvatarImage src={friend.avatar} />
|
||||
<AvatarFallback>{friend.nickname[0]}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="font-medium truncate">
|
||||
{friend.nickname}
|
||||
{friend.remark && <span className="text-gray-500 ml-1">({friend.remark})</span>}
|
||||
<>
|
||||
{friends.map((friend) => (
|
||||
<div
|
||||
key={friend.id}
|
||||
className="flex items-center p-3 border rounded-lg hover:bg-gray-50 cursor-pointer"
|
||||
onClick={() => handleFriendClick(friend)}
|
||||
>
|
||||
<Avatar className="h-10 w-10 mr-3">
|
||||
<AvatarImage src={friend.avatar} />
|
||||
<AvatarFallback>{friend.nickname?.[0] || 'U'}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="font-medium truncate max-w-[180px]">
|
||||
{friend.nickname}
|
||||
{friend.remark && <span className="text-gray-500 ml-1 truncate">({friend.remark})</span>}
|
||||
</div>
|
||||
<ChevronRight className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 truncate">{friend.wechatId}</div>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{friend.tags.slice(0, 3).map((tag: FriendTag) => (
|
||||
<span key={tag.id} className={`text-xs px-2 py-0.5 rounded-full ${tag.color}`}>
|
||||
{tag.name}
|
||||
</span>
|
||||
))}
|
||||
{friend.tags.length > 3 && (
|
||||
<span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-800">
|
||||
+{friend.tags.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<ChevronRight className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 truncate">{friend.wechatId}</div>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{friend.tags.slice(0, 3).map((tag) => (
|
||||
<span key={tag.id} className={`text-xs px-2 py-0.5 rounded-full ${tag.color}`}>
|
||||
{tag.name}
|
||||
</span>
|
||||
))}
|
||||
{friend.tags.length > 3 && (
|
||||
<span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-800">
|
||||
+{friend.tags.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
))}
|
||||
|
||||
{/* 懒加载指示器 */}
|
||||
{hasMoreFriends && (
|
||||
<div ref={friendsLoadingRef} className="py-4 flex justify-center">
|
||||
{isFetchingFriends && <Loader2 className="h-6 w-6 animate-spin text-blue-500" />}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 分页 */}
|
||||
{totalPages > 1 && (
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setCurrentPage((prev) => Math.max(1, prev - 1))
|
||||
}}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||
let pageNumber
|
||||
if (totalPages <= 5) {
|
||||
pageNumber = i + 1
|
||||
} else if (currentPage <= 3) {
|
||||
pageNumber = i + 1
|
||||
} else if (currentPage >= totalPages - 2) {
|
||||
pageNumber = totalPages - 4 + i
|
||||
} else {
|
||||
pageNumber = currentPage - 2 + i
|
||||
}
|
||||
return (
|
||||
<PaginationItem key={pageNumber}>
|
||||
<PaginationLink
|
||||
href="#"
|
||||
isActive={currentPage === pageNumber}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setCurrentPage(pageNumber)
|
||||
}}
|
||||
>
|
||||
{pageNumber}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
})}
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setCurrentPage((prev) => Math.min(totalPages, prev + 1))
|
||||
}}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
)}
|
||||
{/* 显示加载状态和总数 */}
|
||||
<div className="text-sm text-gray-500 text-center">
|
||||
{friendsTotal > 0 ? (
|
||||
<span>
|
||||
已加载 {Math.min(friends.length, friendsTotal)} / {friendsTotal} 条记录
|
||||
</span>
|
||||
) : !isFetchingFriends && !hasFriendLoadError && account ? (
|
||||
<span>
|
||||
共 {account.friendCount} 条记录
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
@@ -770,7 +903,7 @@ export default function WechatAccountDetailPage({ params }: { params: { id: stri
|
||||
标签
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedFriend.tags.map((tag) => (
|
||||
{selectedFriend.tags.map((tag: FriendTag) => (
|
||||
<span key={tag.id} className={`text-sm px-2 py-1 rounded-full ${tag.color}`}>
|
||||
{tag.name}
|
||||
</span>
|
||||
|
||||
@@ -225,7 +225,7 @@ export default function WechatAccountsPage() {
|
||||
{accounts.map((account) => (
|
||||
<Card
|
||||
key={account.id}
|
||||
className="p-4 hover:shadow-lg transition-all cursor-pointer"
|
||||
className="p-4 hover:shadow-lg transition-all cursor-pointer overflow-hidden"
|
||||
onClick={() => router.push(`/wechat-accounts/${account.id}`)}
|
||||
>
|
||||
<div className="flex items-start space-x-4">
|
||||
@@ -236,7 +236,7 @@ export default function WechatAccountsPage() {
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
<h3 className="font-medium truncate">{account.nickname}</h3>
|
||||
<h3 className="font-medium truncate max-w-[180px]">{account.nickname}</h3>
|
||||
<Badge variant={account.status === "normal" ? "outline" : "destructive"}>
|
||||
{account.status === "normal" ? "正常" : "异常"}
|
||||
</Badge>
|
||||
@@ -254,8 +254,8 @@ export default function WechatAccountsPage() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-gray-500 space-y-1">
|
||||
<div>微信号:{account.wechatId}</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="truncate">微信号:{account.wechatId}</div>
|
||||
<div className="flex items-center justify-between flex-wrap gap-1">
|
||||
<div>好友数量:{account.friendCount}</div>
|
||||
<div className="text-green-600">今日新增:+{account.todayAdded}</div>
|
||||
</div>
|
||||
@@ -281,9 +281,9 @@ export default function WechatAccountsPage() {
|
||||
</div>
|
||||
<Progress value={(account.todayAdded / account.maxDailyAdds) * 100} className="h-2" />
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs text-gray-500 pt-2">
|
||||
<div>所属设备:{account.deviceName || '未知设备'}</div>
|
||||
<div>最后活跃:{account.lastActive}</div>
|
||||
<div className="flex items-center justify-between text-xs text-gray-500 pt-2 flex-wrap gap-1">
|
||||
<div className="truncate max-w-[150px]">所属设备:{account.deviceName || '未知设备'}</div>
|
||||
<div className="whitespace-nowrap">最后活跃:{account.lastActive}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Search, RefreshCw } from "lucide-react"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
interface Device {
|
||||
id: string
|
||||
@@ -123,12 +124,15 @@ export function DeviceSelectionDialog({ open, onOpenChange, selectedDevices, onS
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">{device.name}</span>
|
||||
<Badge variant={device.status === "online" ? "success" : "secondary"}>
|
||||
<Badge variant={device.status === "online" ? "default" : "secondary"}>
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 mt-1">
|
||||
<div>IMEI: {device.imei}</div>
|
||||
<div className="flex items-center">
|
||||
<span className="mr-1">IMEI:</span>
|
||||
<ImeiDisplay imei={device.imei} containerWidth={160} />
|
||||
</div>
|
||||
<div>微信号: {device.wxid}</div>
|
||||
</div>
|
||||
{device.usedInPlans > 0 && (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react"
|
||||
import { Card } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Search, RefreshCw, X } from "lucide-react"
|
||||
import { Search, RefreshCw, X, ChevronLeft, ChevronRight } from "lucide-react"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
// 定义类型,避免导入错误
|
||||
interface Device {
|
||||
@@ -148,6 +149,16 @@ export function DeviceSelector({ formData, onChange, onNext, onPrev }: DeviceSel
|
||||
}
|
||||
}
|
||||
|
||||
const handlePrevPage = () => {
|
||||
setCurrentPage((prev) => Math.max(1, prev - 1))
|
||||
}
|
||||
|
||||
const handleNextPage = () => {
|
||||
setCurrentPage((prev) => Math.min(Math.ceil(filteredDevices.length / itemsPerPage), prev + 1))
|
||||
}
|
||||
|
||||
const isLastPage = currentPage === Math.ceil(filteredDevices.length / itemsPerPage)
|
||||
|
||||
return (
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
@@ -193,15 +204,14 @@ export function DeviceSelector({ formData, onChange, onNext, onPrev }: DeviceSel
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="font-medium truncate">{device.name}</div>
|
||||
<div
|
||||
className={`px-2 py-1 rounded-full text-xs ${
|
||||
device.status === "online" ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
<Badge variant={device.status === "online" ? "default" : "secondary"}>
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</div>
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 flex items-center">
|
||||
<span className="mr-1">IMEI:</span>
|
||||
<ImeiDisplay imei={device.imei} containerWidth={160} />
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">IMEI: {device.imei}</div>
|
||||
<div className="text-sm text-gray-500">微信号: {device.wechatId}</div>
|
||||
{device.usedInPlans > 0 && (
|
||||
<div className="text-sm text-orange-500">已用于 {device.usedInPlans} 个计划</div>
|
||||
@@ -211,24 +221,25 @@ export function DeviceSelector({ formData, onChange, onNext, onPrev }: DeviceSel
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<Pagination>
|
||||
<Pagination className="mt-4">
|
||||
<PaginationContent>
|
||||
<PaginationPrevious
|
||||
onClick={() => setCurrentPage((prev) => Math.max(1, prev - 1))}
|
||||
onClick={handlePrevPage}
|
||||
disabled={currentPage === 1}
|
||||
/>
|
||||
{Array.from({ length: Math.ceil(filteredDevices.length / itemsPerPage) }, (_, i) => i + 1).map((page) => (
|
||||
<PaginationItem key={page}>
|
||||
<PaginationLink onClick={() => setCurrentPage(page)} isActive={currentPage === page}>
|
||||
{page}
|
||||
{Array.from({ length: Math.ceil(filteredDevices.length / itemsPerPage) }).map((_, index) => (
|
||||
<PaginationItem key={index}>
|
||||
<PaginationLink
|
||||
onClick={() => setCurrentPage(index + 1)}
|
||||
isActive={currentPage === index + 1}
|
||||
>
|
||||
{index + 1}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))}
|
||||
<PaginationNext
|
||||
onClick={() =>
|
||||
setCurrentPage((prev) => Math.min(Math.ceil(filteredDevices.length / itemsPerPage), prev + 1))
|
||||
}
|
||||
disabled={currentPage === Math.ceil(filteredDevices.length / itemsPerPage)}
|
||||
onClick={handleNextPage}
|
||||
disabled={isLastPage}
|
||||
/>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
@@ -268,6 +279,30 @@ export function DeviceSelector({ formData, onChange, onNext, onPrev }: DeviceSel
|
||||
下一步
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center items-center space-x-2 mt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handlePrevPage}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<div className="text-sm text-gray-500">
|
||||
{currentPage} / {Math.ceil(filteredDevices.length / itemsPerPage)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleNextPage}
|
||||
disabled={isLastPage}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
|
||||
140
Cunkebao/components/ImeiDisplay.tsx
Normal file
140
Cunkebao/components/ImeiDisplay.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription
|
||||
} from "@/components/ui/dialog"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Copy } from "lucide-react"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
|
||||
interface ImeiDisplayProps {
|
||||
imei: string
|
||||
className?: string
|
||||
containerWidth?: string | number // 可以接受字符串或数字类型的容器宽度配置
|
||||
}
|
||||
|
||||
export function ImeiDisplay({ imei, className = "", containerWidth = "max-w-[calc(100%-40px)]" }: ImeiDisplayProps) {
|
||||
// 初始状态设为false,确保服务端渲染和客户端水合时状态一致
|
||||
const [open, setOpen] = useState(false)
|
||||
const [mounted, setMounted] = useState(false)
|
||||
|
||||
// 确保只在客户端执行的初始化逻辑
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
const handleCopy = () => {
|
||||
// 只在客户端执行
|
||||
if (typeof navigator === 'undefined') return
|
||||
|
||||
try {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(imei)
|
||||
toast({
|
||||
title: "已复制",
|
||||
description: "IMEI已复制到剪贴板",
|
||||
})
|
||||
} else {
|
||||
// 备用复制方法
|
||||
const textArea = document.createElement("textarea")
|
||||
textArea.value = imei
|
||||
document.body.appendChild(textArea)
|
||||
textArea.focus()
|
||||
textArea.select()
|
||||
|
||||
try {
|
||||
document.execCommand('copy')
|
||||
toast({
|
||||
title: "已复制",
|
||||
description: "IMEI已复制到剪贴板",
|
||||
})
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: "复制失败",
|
||||
description: "您的浏览器不支持自动复制",
|
||||
variant: "destructive"
|
||||
})
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea)
|
||||
}
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: "复制失败",
|
||||
description: "您的浏览器不支持自动复制",
|
||||
variant: "destructive"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 处理Dialog打开和关闭
|
||||
const handleOpenChange = (newOpen: boolean) => {
|
||||
// 如果是关闭操作,阻止事件冒泡
|
||||
if (!newOpen) {
|
||||
// 使用setTimeout确保事件处理完成后再关闭模态框
|
||||
setTimeout(() => {
|
||||
setOpen(false)
|
||||
}, 0)
|
||||
} else {
|
||||
setOpen(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理containerWidth为数字的情况
|
||||
const widthStyle = typeof containerWidth === 'number'
|
||||
? `max-w-[${containerWidth}px]`
|
||||
: containerWidth
|
||||
|
||||
// 服务端渲染时,仅返回静态内容
|
||||
if (!mounted) {
|
||||
return <span className={`truncate inline-block ${widthStyle} ${className}`}>{imei}</span>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
className={`cursor-pointer hover:text-blue-600 truncate inline-block imei-display ${widthStyle} ${className}`}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setOpen(true)
|
||||
// 防止冒泡到Card的点击事件
|
||||
return false
|
||||
}}
|
||||
title={imei}
|
||||
>
|
||||
{imei}
|
||||
</span>
|
||||
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent className="sm:max-w-md" onClick={(e) => e.stopPropagation()}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>设备IMEI</DialogTitle>
|
||||
<DialogDescription>
|
||||
完整的设备唯一标识符
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex items-center space-x-2 bg-gray-50 p-3 rounded-md">
|
||||
<code className="flex-1 text-sm break-all">{imei}</code>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleCopy()
|
||||
}}
|
||||
title="复制IMEI"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
import { Card } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Smartphone, Battery, Users, MessageCircle } from "lucide-react"
|
||||
import { Smartphone, Battery, Users, MessageCircle, Clock } from "lucide-react"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { ImeiDisplay } from "@/components/ImeiDisplay"
|
||||
|
||||
export interface Device {
|
||||
id: string
|
||||
@@ -54,7 +55,7 @@ export function DeviceGrid({ devices, selectable, selectedDevices, onSelect, dev
|
||||
)}
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Badge variant={currentStatus.status === "online" ? "success" : "secondary"}>
|
||||
<Badge variant={currentStatus.status === "online" ? "default" : "secondary"}>
|
||||
{currentStatus.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
<div className="flex items-center space-x-2">
|
||||
@@ -68,7 +69,10 @@ export function DeviceGrid({ devices, selectable, selectedDevices, onSelect, dev
|
||||
<Smartphone className="w-4 h-4 text-gray-400" />
|
||||
<div>
|
||||
<div className="text-sm font-medium">{device.name}</div>
|
||||
<div className="text-xs text-gray-500">IMEI-{device.imei}</div>
|
||||
<div className="text-xs text-gray-500 flex items-center">
|
||||
<span className="mr-1">IMEI:</span>
|
||||
<ImeiDisplay imei={device.imei} containerWidth={80} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{device.remark && <div className="text-xs text-gray-500">备注: {device.remark}</div>}
|
||||
@@ -90,7 +94,7 @@ export function DeviceGrid({ devices, selectable, selectedDevices, onSelect, dev
|
||||
<div>今日添加:{device.todayAdded}</div>
|
||||
<div>
|
||||
加友状态:
|
||||
<Badge variant={device.addFriendStatus === "normal" ? "success" : "destructive"}>
|
||||
<Badge variant={device.addFriendStatus === "normal" ? "default" : "destructive"}>
|
||||
{device.addFriendStatus === "normal" ? "正常" : "异常"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
1
Server/.gitignore
vendored
1
Server/.gitignore
vendored
@@ -7,4 +7,3 @@ public/upload
|
||||
vendor
|
||||
/public/.user.ini
|
||||
/404.html
|
||||
thinkphp
|
||||
|
||||
@@ -1,927 +0,0 @@
|
||||
## V5.1.39 LTS(2019-11-18)
|
||||
|
||||
本次更新为常规更新,主要包括:
|
||||
|
||||
* 修正`memcached`驱动
|
||||
* 改进`HasManyThrough`关联查询
|
||||
* 改进`Request`类`isJson`方法
|
||||
* 改进关联查询
|
||||
* 改进`redis`驱动
|
||||
* 增加 Model类`getWhere`方法对复合主键的支持
|
||||
* 改进`newQuery`方法
|
||||
* 改进闭包查询的参数绑定
|
||||
* 修正`Validate`
|
||||
* 修复某些情况下URL会多一个冒号
|
||||
* 调整composer.json
|
||||
* 修复使用`Cache::clear()`时,报错缓存文件不存在问题
|
||||
* 使用File类的unlink方法进行文件删除
|
||||
* 改进`paraseData`方法
|
||||
* 修正image验证方法
|
||||
* 改进Url生成
|
||||
* 改进空操作对数字的支持
|
||||
* 改进一处PHP7.4兼容性问题
|
||||
|
||||
## V5.1.38 LTS(2019-8-8)
|
||||
|
||||
本次更新为常规更新,主要包括:
|
||||
|
||||
* `Request`类增加`isJson`方法
|
||||
* 改进浮点型查询
|
||||
* 修正关联查询关联外键为空的查询错误
|
||||
* 远程一对多支持关联统计和预载入查询
|
||||
* 远程一对多关联支持`has`/`hasWhere`查询
|
||||
* 优化`parseIn`解析
|
||||
* 改进`parseLike`查询
|
||||
* 改进Url生成
|
||||
* 改进模型的`toArray`方法
|
||||
* 修正`notIn`查询
|
||||
* 改进`JSON`字段查询
|
||||
* 改进Controller类`display`/`fetch`方法返回`ViewResponse`对象
|
||||
* 改进`param`方法
|
||||
* 改进`mysql`驱动`getExplain`方法
|
||||
* 改进时间查询
|
||||
* 改进模型关联的`has`/`hasWhere`方法对软删除的支持
|
||||
* 修正社区反馈的BUG
|
||||
|
||||
## V5.1.37 LTS(2019-5-26)
|
||||
|
||||
本次更新为常规更新,主要更新如下:
|
||||
|
||||
* 改进关联数据更新
|
||||
* 修正关联动态获取器
|
||||
* 改进`redis`驱动
|
||||
* 修复验证规则里面出现二维数组时的错误
|
||||
* 改进跨域请求支持
|
||||
* 完善模型`hidden`方法对关联属性的支持
|
||||
* 改进`where`查询方法传入`Query`对象的支持`bind`数据
|
||||
* 改进数据集对象的`load`方法
|
||||
* 修正缓存类`clear`方法对`tag`的支持
|
||||
|
||||
## V5.1.36 LTS(2019-4-28)
|
||||
|
||||
本次更新为常规更新,主要更新如下:
|
||||
|
||||
* 修正`chunk`方法一处异常抛出的错误
|
||||
* 修正模型输出的`visible`
|
||||
* 改进环境变量加载
|
||||
* 改进命令行日志的`level`配置支持
|
||||
* 修复设置有缓存前缀时,无法清空缓存标签的问题
|
||||
* HasMony对象`saveAll`方法兼容`Collection`格式参数格式
|
||||
* 修正`whereOr`查询使用字符串的问题
|
||||
* 改进`dateFormat`设置对写入数据的影响
|
||||
* 修正查询缓存
|
||||
* 记住指定的跳转地址
|
||||
* 改进软删除
|
||||
* 改进聚合查询SQL去除limit 1
|
||||
* 改进缓存驱动
|
||||
|
||||
## V5.1.35 LTS(2019-3-2)
|
||||
|
||||
本次主要为常规更新,修正了一些反馈的问题。
|
||||
|
||||
* 修正验证类自定义验证方法执行两次的问题
|
||||
* 模型增加`isEmpty`方法用于判断是否空模型
|
||||
* 改进获取器对`append`的支持
|
||||
* 修正一对多关联的`withCount`自关联问题
|
||||
* facade类注释调整
|
||||
* 改进关联属性的`visible`和`hidden`判断
|
||||
* 修正路由分组的`MISS`路由
|
||||
* 改进pgsql.sql
|
||||
|
||||
## V5.1.34 LTS(2019-1-30)
|
||||
|
||||
本次更新为常规更新,修正了一些反馈的问题。
|
||||
|
||||
* 改进Request类的`has`方法,支持`patch`
|
||||
* 改进`unique`验证的多条件支持
|
||||
* 修复自定义上传验证,检测文件大小
|
||||
* 改进`in`查询支持表达式
|
||||
* 改进路由的`getBind`方法
|
||||
* 改进验证类的错误信息获取
|
||||
* 改进`response`助手函数默认值
|
||||
* 修正mysql的`regexp`查询
|
||||
* 改进模型类型强制转换写入对`Expression`对象的支持
|
||||
|
||||
## V5.1.33 LTS(2019-1-16)
|
||||
|
||||
* 修复路由中存在多个相同替换的正则BUG
|
||||
* 修正whereLike查询
|
||||
* join方法支持参数绑定
|
||||
* 改进union方法
|
||||
* 修正多对多关联的attach方法
|
||||
* 改进验证类的正则规则自定义
|
||||
* 改进Request类method方法
|
||||
* 改进File日志类型的CLI日志写入
|
||||
* 改进文件日志time_format配置对JSON格式的支持
|
||||
|
||||
## V5.1.32 LTS(2018-12-24)
|
||||
|
||||
本次主要为常规更新,修正了一些反馈的问题。
|
||||
|
||||
|
||||
* 改进多对多关联的`attach`方法
|
||||
* 改进聚合查询的`field`处理
|
||||
* 改进关联的`save`方法
|
||||
* 修正模型`exists`方法返回值
|
||||
* 改进时间字段写入和输出
|
||||
* 改进控制器中间件的调用
|
||||
* 改进路由变量替换的性能
|
||||
* 改进缓存标签的处理机制
|
||||
|
||||
## V5.1.31 LTS (2018-12-9)
|
||||
|
||||
本次版本包含一个安全更新,建议升级。
|
||||
|
||||
* 改进`field`方法
|
||||
* 改进`count`方法返回类型
|
||||
* `download`函数增加在浏览器中显示文件功能
|
||||
* 修正多对多模型的中间表数据写入
|
||||
* 改进`sqlsrv`驱动支持多个Schemas模式查询
|
||||
* 统一助手函数与\think\response\Download函数文件过期时间
|
||||
* 完善关联模型的`save`方法 增加`make`方法仅创建对象不保存
|
||||
* 修改条件表达式对静态变量的支持
|
||||
* 修正控制器名获取
|
||||
* 改进view方法的`field`解析
|
||||
|
||||
## V5.1.30 LTS(2018-11-30)
|
||||
|
||||
该版本为常规更新,修正了一些社区反馈的问题。
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 改进查询类的`execute`方法
|
||||
* 判断路由规则定义添加对请求类型的判断
|
||||
* 修复`orderRaw`异常
|
||||
* 修正 `optimize:autoload`指令
|
||||
* 改进软删除的`destroy`方法造成重复执行事件的问题
|
||||
* 改进验证类对扩展验证规则 始终验证 不管是否`require`
|
||||
* 修复自定义验证`remove`所有规则的异常
|
||||
* 改进时间字段的自动写入支持微秒数据
|
||||
* 改进`Connection`类的`getrealsql`方法
|
||||
* 修正`https`地址的URL生成
|
||||
* 修复 `array_walk_recursive` 在低于PHP7.1消耗内部指针问题
|
||||
* 改进手动参数绑定使用
|
||||
* 改进聚合查询方法的`field`参数支持`Expression`
|
||||
|
||||
## V5.1.29 LTS(2018-11-11)
|
||||
|
||||
该版本主要改进了参数绑定的解析问题和提升性能,并修正了一些反馈的问题。
|
||||
|
||||
* 改进手动参数绑定
|
||||
* 修正MISS路由的分组参数无效问题
|
||||
* 行为支持对象的方法
|
||||
* 修正全局查询范围
|
||||
* 改进`belongsto`关联的`has`方法
|
||||
* 改进`hasMany`关联
|
||||
* 改进模型观察者多次注册的问题
|
||||
* 改进`query`类的默认查询参数处理
|
||||
* 修正`parseBetween`解析方法
|
||||
* 改进路由地址生成的本地域名支持
|
||||
* 改进参数绑定的实际URL解析性能
|
||||
* 改进`Env`类的`getEnv`和`get`方法
|
||||
* 改进模板缓存的生成优化
|
||||
* 修复验证类的多语言支持
|
||||
* 修复自定义场景验证`remove`规则异常
|
||||
* File类添加是否自动补全扩展名的选项
|
||||
* 改进`strpos`对子串是否存在的判断
|
||||
* 修复`choice`无法用值选择第一个选项问题
|
||||
* 验证器支持多维数组取值验证
|
||||
* 改进解析`extend`和`block`标签的正则
|
||||
|
||||
## V5.1.28 LTS(2018-10-29)
|
||||
|
||||
该版本主要修正了上一个版本存在的一些问题,并改进了关联查询
|
||||
|
||||
* 改进聚合查询方法的字段支持DISTINCT
|
||||
* 改进定义路由后url函数的端口生成
|
||||
* 改进控制器中间件对`swoole`等的支持
|
||||
* 改进Log类`save`方法
|
||||
* 改进验证类的闭包验证参数
|
||||
* 多对多关联支持指定中间表数据的名称
|
||||
* 关联聚合查询支持闭包方式指定聚合字段
|
||||
* 改进Lang类`get`方法
|
||||
* 多对多关联增加判断关联数据是否存在的方法
|
||||
* 改进关联查询使用`fetchsql`的情况
|
||||
* 改进修改器的是否已经执行判断
|
||||
* 增加`afterWith`和`beforeWith`验证规则 用于比较日期字段
|
||||
|
||||
## V5.1.27 LTS(2018-10-22)
|
||||
|
||||
该版本主要修正了路由绑定的参数,改进了修改器的执行多次问题,并正式宣布为LTS版本!
|
||||
|
||||
|
||||
* 修正路由绑定的参数丢失问题
|
||||
* 修正路由别名的参数获取
|
||||
* 改进修改器会执行多次的问题
|
||||
|
||||
## V5.1.26(2018-10-12)
|
||||
|
||||
该版本主要修正了上一个版本的一些问题,并改进了全局查询范围的支持,同时包含了一个安全更新。
|
||||
|
||||
|
||||
* 修正单一模块下注解路由无效的问题
|
||||
* 改进数据库的聚合查询的字段处理
|
||||
* 模型类增加`globalScope`属性定义 用于指定全局的查询范围
|
||||
* 模型的`useGlobalScope`方法支持传入数组 用于指定当前查询需要使用的全局查询范围
|
||||
* 改进数据集的`order`方法对数字类型的支持
|
||||
* 修正上一个版本`order`方法解析的一处BUG
|
||||
* 排序字段不合法或者错误的时候抛出异常
|
||||
* 改进`Request`类的`file`方法对上传文件的错误判断
|
||||
|
||||
## V5.1.25(2018-9-21)
|
||||
|
||||
该版本主要改进了查询参数绑定的性能和对浮点型的支持,以及一些细节的完善。
|
||||
|
||||
* 修正一处命令行问题
|
||||
* 改进`Socketlog`日志驱动,支持自定义默认展开日志类别
|
||||
* 修正`MorphMany`一处bug
|
||||
* 跳转到上次记住的url,并支持默认值
|
||||
* 改进模型的异常提示
|
||||
* 改进参数绑定对浮点型的支持
|
||||
* 改进`order`方法解析
|
||||
* 改进`json`字段数据的自动编码
|
||||
* 改进日志`log_write`可能造成的日志写入死循环
|
||||
* Log类增加`log_level`行为标签位置,用于对某个类型的日志进行处理
|
||||
* Route类增加`clear`方法清空路由规则
|
||||
* 分布式数据库配置支持使用数组
|
||||
* 单日志文件也支持`max_files`参数
|
||||
* 改进查询参数绑定的性能
|
||||
* 改进别名路由的URL后缀参数检测
|
||||
* 控制器前置方法和控制器中间件的`only`和`except`定义不区分大小写
|
||||
|
||||
## V5.1.24(2018-9-5)
|
||||
|
||||
该版本主要增加了命令行的表格输出功能,并增加了查看路由定义的指令,以及修正了社区的一些反馈问题。
|
||||
|
||||
* 修正`Request`类的`file`方法
|
||||
* 修正路由的`cache`方法
|
||||
* 修正路由缓存的一处问题
|
||||
* 改进上传文件获取的异常处理
|
||||
* 改进`fetchCollection`方法支持传入数据集类名
|
||||
* 修正多级控制器的注解路由生成
|
||||
* 改进`Middleware`类`clear`方法
|
||||
* 增加`route:list`指令用于[查看定义的路由](752690) 并支持排序
|
||||
* 命令行增加`Table`输出类
|
||||
* `Command`类增加`table`方法用于输出表格
|
||||
* 改进搜索器查询方法支持别名定义
|
||||
* 命令行配置增加`auto_path`参数用于定义自动载入的命令类路径
|
||||
* 增加`make:command`指令用于[快速生成指令](354146)
|
||||
* 改进`make:controller`指令对操作方法后缀的支持
|
||||
* 改进命令行的定义文件支持索引数组 用于指令对象的惰性加载
|
||||
* 改进`value`和`column`方法对后续查询结果的影响
|
||||
* 改进`RuleName`类的`setRule`方法
|
||||
|
||||
## V5.1.23(2018-8-23)
|
||||
|
||||
该版本主要改进了数据集对象的处理,增加了`findOrEmpty`方法,并且修正了一些社区反馈的BUG。
|
||||
|
||||
* 数据集类增加`diff`/`intersect`方法用于获取差集和交集(默认根据主键值比较)
|
||||
* 数据集类增加`order`方法支持指定字段排序
|
||||
* 数据集类增加`map`方法使用回调函数处理数据并返回新的数据集对象
|
||||
* Db增加`allowEmpty`方法允许`find`方法在没有数据的时候返回空数组或者空模型对象而不是null
|
||||
* Db增加`findOrEmpty`方法
|
||||
* Db增加`fetchCollection`方法用于指定查询返回数据集对象
|
||||
* 改进`order`方法的数组方式解析,增强安全性
|
||||
* 改进`withSearch`方法,支持第三个参数传入字段前缀标识,用于多表查询字段搜索
|
||||
* 修正`optimize:route`指令开启类库后缀后的注解路由生成
|
||||
* 修正redis缓存及session驱动
|
||||
* 支持指定`Yaconf`的独立配置文件
|
||||
* 增加`yaconf`助手函数用于配置文件
|
||||
|
||||
|
||||
## V5.1.22(2018-8-9)
|
||||
|
||||
该版本主要增加了模型搜索器和`withJoin`方法,完善了模型输出和对`Yaconf`的支持,修正了一些社区反馈的BUG。
|
||||
|
||||
* 改进一对一关联的`table`识别问题
|
||||
* 改进内置`Facade`类
|
||||
* 增加`withJoin`方法支持`join`方式的[一对一关联](一对一关联.md)查询
|
||||
* 改进`join`预载入查询的空数据问题
|
||||
* 改进`Config`类的`load`方法支持快速加载配置文件
|
||||
* 改进`execute`方法和事务的断线重连
|
||||
* 改进`memcache`驱动的`has`方法
|
||||
* 模型类支持定义[搜索器](搜索器.md)方法
|
||||
* 完善`Config`类对`Yaconf`的支持
|
||||
* 改进模型的`hidden/visible/append/withAttr`方法,支持在[查询前后调用](数组访问.md),以及支持数据集对象
|
||||
* 数据集对象增加`where`方法根据字段或者关联数据[过滤数据](模型数据集.md)
|
||||
* 改进AJAX请求的`204`判断
|
||||
|
||||
|
||||
## V5.1.21(2018-8-2)
|
||||
|
||||
该版本主要增加了下载响应对象和数组查询对象的支持,并修正了一些社区反馈的问题。
|
||||
|
||||
* 改进核心对象的无用信息调试输出
|
||||
* 改进模型的`isRelationAttr`方法判断
|
||||
* 模型类的`get`和`all`方法并入Db类
|
||||
* 增加[下载响应对象](文件下载.md)和`download`助手函数
|
||||
* 修正别名路由配置定义读取
|
||||
* 改进`resultToModel`方法
|
||||
* 修正开启类库后缀后的注解路由生成
|
||||
* `Response`类增加`noCache`快捷方法
|
||||
* 改进路由对象在`Swoole`/`Workerman`下面参数多次合并问题
|
||||
* 修正路由`ajax`/`pjax`参数后路由变量无法正确获取的问题
|
||||
* 增加清除中间件的方法
|
||||
* 改进依赖注入的参数规范自动识别(便于对接前端小写+下划线规范)
|
||||
* 改进`hasWhere`的数组条件的字段判断
|
||||
* 增加[数组查询对象](高级查询.md)`Where`支持(喜欢数组查询的福音)
|
||||
* 改进多对多关联的闭包支持
|
||||
|
||||
## V5.1.20(2018-7-25)
|
||||
|
||||
该版本主要增加了Db和模型的动态获取器的支持,并修正了一些已知问题。
|
||||
|
||||
* Db类添加[获取器支持](703981)
|
||||
* 支持模型及关联模型字段[动态定义获取器](354046)
|
||||
* 动态获取器支持`JSON`字段
|
||||
* 改进路由的`before`行为执行(匹配后执行)
|
||||
* `Config`类支持`Yaconf`
|
||||
* 改进Url生成的端口问题
|
||||
* Request类增加`setUrl`和`setBaseUrl`方法
|
||||
* 改进页面trace的信息显示
|
||||
* 修正`MorphOne`关联
|
||||
* 命令行添加[查看版本指令](703994)
|
||||
|
||||
## V5.1.19 (2018-7-13)
|
||||
|
||||
该版本是一个小幅改进版本,针对`Swoole`和`Workerman`的`Cookie`支持做了一些改进,并修正了一些已知的问题。
|
||||
|
||||
|
||||
* 改进query类`delete`方法对软删除条件判断
|
||||
* 修正分表查询的软删除问题
|
||||
* 模型查询的时候同时传入`table`和`name`属性
|
||||
* 容器类增加`IteratorAggregate`和`Countable`接口支持
|
||||
* 路由分组支持对下面的资源路由统一设置`only/except/vars`参数
|
||||
* 改进Cookie类更好支持扩展
|
||||
* 改进Request类`post`方法
|
||||
* 改进模型自关联的自动识别
|
||||
* 改进Request类对`php://input`数据的处理
|
||||
|
||||
|
||||
## V5.1.18 (2018-6-30)
|
||||
|
||||
该版本主要完善了对`Swoole`和`Workerman`的`HttpServer`运行支持,改进`Request`类,并修正了一些已知的问题。
|
||||
|
||||
* 改进关联`append`方法的处理
|
||||
* 路由初始化和检测方法分离
|
||||
* 修正`destroy`方法强制删除
|
||||
* `app_init`钩子位置移入`run`方法
|
||||
* `think-swoole`扩展更新到2.0版本
|
||||
* `think-worker`扩展更新到2.0版本
|
||||
* 改进Url生成的域名自动识别
|
||||
* `Request`类增加`setPathinfo`方法和`setHost`方法
|
||||
* `Request`类增加`withGet`/`withPost`/`withHeader`/`withServer`/`withCookie`/`withEnv`方法进行赋值操作
|
||||
* Route类改进`host`属性的获取
|
||||
* 解决注解路由配置不生效的问题
|
||||
* 取消Test日志驱动,改为使用`close`设置关闭全局日志写入
|
||||
* 修正路由的`response`参数
|
||||
* 修正204响应输出的判断
|
||||
|
||||
## V5.1.17 (2018-6-18)
|
||||
|
||||
该版本主要增加了控制器中间件的支持,改进了路由功能,并且修正了社区反馈的一些问题。
|
||||
|
||||
* 修正软删除的`delete`方法
|
||||
* 修正Query类`Count`方法
|
||||
* 改进多对多`detach`方法
|
||||
* 改进Request类`Session`方法
|
||||
* 增加控制器中间件支持
|
||||
* 模型类增加`jsonAssoc`属性用于定义json数据是否返回数组
|
||||
* 修正Request类`method`方法的请求伪装
|
||||
* 改进静态路由的匹配
|
||||
* 分组首页路由自动完整匹配
|
||||
* 改进sqlsrv的`column`方法
|
||||
* 日志类的`apart_level`配置支持true自动生成对应类型的日志文件
|
||||
* 改进`204`输出判断
|
||||
* 修正cli下页面输出的BUG
|
||||
* 验证类使用更高效的`ctype`验证机制
|
||||
* 改进Request类`cookie`方法
|
||||
* 修正软删除的`withTrashed`方法
|
||||
* 改进多态一对多的预载入查询
|
||||
* 改进Query类`column`方法的缓存读取
|
||||
* Query类增加`whereBetweenTimeField`方法
|
||||
* 改进分组下多个相同路由规则的合并匹配问题
|
||||
* 路由类增加`getRule`/`getRuleList`方法获取定义的路由
|
||||
|
||||
## V5.1.16 (2018-6-7)
|
||||
|
||||
该版本主要修正了社区反馈的一些问题,并对Request类做了进一步规范和优化。
|
||||
|
||||
* 改进Session类的`boot`方法
|
||||
* App类的初始化方法可以单独执行
|
||||
* 改进Request类的`param`方法
|
||||
* 改进资源路由的变量替换
|
||||
* Request类增加`__isset`方法
|
||||
* 改进`useGlobalScope`方法对软删除的影响
|
||||
* 修正命令行调用
|
||||
* 改进Cookie类`init`方法
|
||||
* 改进多对多关联删除的返回值
|
||||
* 一对多关联写入支持`replace`
|
||||
* 路由增加`filter`检测方法,用于通过请求参数检测路由是否匹配
|
||||
* 取消Request类`session/env/server`方法的`filter`参数
|
||||
* 改进关联的指定属性输出
|
||||
* 模型删除操作删除后不清空对象数据仅作标记
|
||||
* 调整模型的`save`方法返回值为布尔值
|
||||
* 修正Request类`isAjax`方法
|
||||
* 修正中间件的模块配置读取
|
||||
* 取消Request类的请求变量的设置功能
|
||||
* 取消请求变量获取的默认修饰符
|
||||
* Request类增加`setAction/setModule/setController`方法
|
||||
* 关联模型的`delete`方法调用Query类
|
||||
* 改进URL生成的域名识别
|
||||
* 改进URL检测对已定义路由的域名判断
|
||||
* 模型类增加`isExists`和`isForce`方法
|
||||
* 软删除的`destroy`和`restore`方法返回值调整为布尔值
|
||||
|
||||
## V5.1.15 (2018-6-1)
|
||||
|
||||
该版本主要改进了路由缓存的性能和缓存方式设置,增加了JSON格式文件日志的支持,并修正了社区反馈的一些问题。
|
||||
|
||||
* 容器类增加`exists`方法 仅判断是否存在对象实例
|
||||
* 取消配置类的`autoload`方法
|
||||
* 改进路由缓存大小提高性能
|
||||
* 改进Dispatch类`init`方法
|
||||
* 增加`make:validate`指令生成验证器类
|
||||
* Config类`get`方法支持默认值参数
|
||||
* 修正字段缓存指令
|
||||
* 改进App类对`null`数据的返回
|
||||
* 改进模型类的`__isset`方法判断
|
||||
* 修正`Query`类的`withAggregate`方法
|
||||
* 改进`RuleItem`类的`setRuleName`方法
|
||||
* 修正依赖注入和参数的冲突问题
|
||||
* 修正Db类对第三方驱动的支持
|
||||
* 修正模型类查询对象问题
|
||||
* 修正File缓存驱动的`has`方法
|
||||
* 修正资源路由嵌套
|
||||
* 改进Request类对`$_SERVER`变量的读取
|
||||
* 改进请求缓存处理
|
||||
* 路由缓存支持指定单独的缓存方式和参数
|
||||
* 修正资源路由的中间件多次执行问题
|
||||
* 修正`optimize:config`指令
|
||||
* 文件日志支持`JSON`格式日志保存
|
||||
* 修正Db类`connect`方法
|
||||
* 改进Log类`write`方法不会自动写入之前日志
|
||||
* 模型的关联操作默认启用事务
|
||||
* 改进软删除的事件响应
|
||||
|
||||
## V5.1.14 (2018-5-18)
|
||||
|
||||
该版本主要对底层容器进行了一些优化改进,并增加了路由缓存功能,可以进一步提升路由性能。
|
||||
|
||||
* 依赖注入的对象参数传入改进
|
||||
* 改进核心类的容器实例化
|
||||
* 改进日期字段的读取
|
||||
* 改进验证类的`getScene`方法
|
||||
* 模型的`create`方法和`save`方法支持`replace`操作
|
||||
* 改进`Db`类的调用机制
|
||||
* App类调整为容器类
|
||||
* 改进容器默认绑定
|
||||
* `Loader`类增加工厂类的实例化方法
|
||||
* 增加路由变量默认规则配置参数
|
||||
* 增加路由缓存设计
|
||||
* 错误处理机制改进
|
||||
* 增加清空路由缓存指令
|
||||
|
||||
|
||||
## V5.1.13 (2018-5-11)
|
||||
|
||||
该版本主要增加了MySQL的XA事务支持,模型事件支持观察者,以及对Facade类的改进。
|
||||
|
||||
* 改进自动缓存
|
||||
* 改进Url生成
|
||||
* 修正数据缓存
|
||||
* 修正`value`方法的缓存
|
||||
* `join`方法和`view`方法的条件支持使用`Expression`对象
|
||||
* 改进驱动的`parseKey`方法
|
||||
* 改进Request类`host`方法和`domain`方法对端口的处理
|
||||
* 模型增加`withEvent`方法用于控制当前操作是否需要执行模型事件
|
||||
* 模型`setInc/setDec`方法支持更新事件
|
||||
* 模型添加`before_restore/after_restore`事件
|
||||
* 增加模型事件观察者
|
||||
* 路由增加`mobile`方法设置是否允许手机访问
|
||||
* 数据库XA事务支持
|
||||
* 改进索引数组查询对`IN`查询的支持
|
||||
* 修正`invokeMethod`方法
|
||||
* 修正空数据写入返回值的BUG
|
||||
* redis驱动支持`predis`
|
||||
* 改进`parseData`方法
|
||||
* 改进模块加载
|
||||
* App类初始化方法调整
|
||||
* 改进数组查询对表达式`Expression`对象支持
|
||||
* 改进闭包的依赖注入调用
|
||||
* 改进多对多关联的中间表模型更新
|
||||
* 增加容器中对象的自定义实例化
|
||||
|
||||
## V5.1.12 (2018-4-25)
|
||||
|
||||
该版本主要改进了主从查询的及时性,并支持动态设置请求数据。
|
||||
|
||||
* 支持动态设置请求数据
|
||||
* 改进`comment`方法解析
|
||||
* 修正App类`__unset`方法
|
||||
* 改进url生成的域名绑定
|
||||
* 改进主从查询的及时性
|
||||
* 修正`value`的数据缓存功能
|
||||
* 改进分页类的集合对象方法调用
|
||||
* 改进Db类的代码提示
|
||||
* SQL日志增加主从标记
|
||||
|
||||
## V5.1.11 (2018-4-19)
|
||||
|
||||
该版本为安全和修正版本,改进了JSON查询的参数绑定问题和容器类对象实例获取,并包含一处可能的安全隐患,建议更新。
|
||||
|
||||
* 支持指定JSON数据查询的字段类型
|
||||
* 修正`selectInsert`方法
|
||||
* `whereColumn`方法支持数组方式
|
||||
* 改进容器类`make`方法
|
||||
* 容器类`delete`方法支持数组
|
||||
* 改进`composer`自动加载
|
||||
* 改进模板引擎
|
||||
* 修正`like`查询的一处安全隐患
|
||||
|
||||
## V5.1.10 (2018-4-16)
|
||||
|
||||
该版本为修正版本,修正上一个版本的一些BUG,并增强了`think clear`指令。
|
||||
|
||||
* 改进`orderField`方法
|
||||
* 改进`exists`查询
|
||||
* 修改cli模式入口文件位置计算
|
||||
* 修正`null`查询
|
||||
* 改进`parseTime`方法
|
||||
* 修正关联预载入查询
|
||||
* 改进`mysql`驱动
|
||||
* 改进`think clear`指令 支持 `-c -l -r `选项
|
||||
* 改进路由规则对`/`结尾的支持
|
||||
|
||||
## V5.1.9 (2018-4-12)
|
||||
|
||||
该版本主要是一些改进和修正,并包含一个安全更新,是一个推荐更新版本。
|
||||
|
||||
* 默认模板渲染规则支持配置保持操作方法名
|
||||
* 改进`Request`类的`ip`方法
|
||||
* 支持模型软删除字段的默认值定义
|
||||
* 改进路由变量规则对中文的支持
|
||||
* 使用闭包查询的时候使用`cache(true)` 抛出异常提示
|
||||
* 改进`Loader`类`loadComposerAutoloadFiles`方法
|
||||
* 改进查询方法安全性
|
||||
* 修正路由地址中控制器名驼峰问题
|
||||
* 调整上一个版本的`module_init`和`app_begin`的钩子顺序问题
|
||||
* 改进CLI命令行执行的问题
|
||||
* 修正社区反馈的其它问题
|
||||
|
||||
## V5.1.8 (2018-4-5)
|
||||
|
||||
该版本主要改进了中间件的域名和模块支持,并同时修正了几个已知问题。
|
||||
|
||||
* 增加`template.auto_rule` 参数设置默认模板渲染的操作名自动转换规则
|
||||
* 默认模板渲染规则改由视图驱动实现
|
||||
* 修正路由标识定义
|
||||
* 修正控制器路由方法
|
||||
* 改进Request类`ip`方法支持自定义代理IP参数
|
||||
* 路由注册中间件支持数组方式别名
|
||||
* 改进命令行执行下的`composer`自动加载
|
||||
* 添加域名中间件注册支持
|
||||
* 全局中间件支持模块定义文件
|
||||
* Log日志配置支持`close`参数可以全局关闭日志写入
|
||||
* 中间件方法中捕获`HttpResponseException`异常
|
||||
* 改进中间件的闭包参数传入
|
||||
* 改进分组路由的延迟解析
|
||||
* 改进URL生成对域名绑定的支持
|
||||
* 改进文件缓存和文件日志驱动的并发支持
|
||||
|
||||
## V5.1.7 (2018-3-28)
|
||||
|
||||
该版本主要修正了路由的一些问题,并改进了查询的安全性。
|
||||
|
||||
* 支持`middleware`配置文件预先定义中间件别名方便路由调用
|
||||
* 修正资源路由
|
||||
* 改进`field`方法 自动识别`fieldRaw`
|
||||
* 增加`Expression`类
|
||||
* Query类增加`raw`方法
|
||||
* Query类的`field`/ `order` 和` where`方法都支持使用`raw`表达式查询
|
||||
* 改进`inc/dec`查询 支持批量更新
|
||||
* 改进路由分组
|
||||
* 改进Response类`create`方法
|
||||
* 改进composer自动加载
|
||||
* 修正域名路由的`append`方法
|
||||
* 修正操作方法的初始化方法获取不到问题
|
||||
|
||||
## V5.1.6 (2018-3-26)
|
||||
|
||||
该版本主要改进了路由规则的匹配算法,大幅提升了路由性能。并正式引入了中间件的支持,可以在路由中定义或者全局定义。另外包含了一个安全更新,是一个建议更新版本。
|
||||
|
||||
* 改进URL生成对路由`ext`方法的支持
|
||||
* 改进查询缓存对不同数据库相同表名的支持
|
||||
* 改进composer自动加载的性能
|
||||
* 改进空路由变量对默认参数的影响
|
||||
* mysql的`json`字段查询支持多级
|
||||
* Query类增加`option`方法
|
||||
* 优化路由匹配
|
||||
* 修复验证规则数字键名丢失问题
|
||||
* 改进路由Url生成
|
||||
* 改进一对一关联预载入查询
|
||||
* Request类增加`rootDomain`方法
|
||||
* 支持API资源控制器生成 `make:controller --api`
|
||||
* 优化Template类的标签解析
|
||||
* 容器类增加删除和清除对象实例的方法
|
||||
* 修正MorphMany关联的`eagerlyMorphToMany`方法一处错误
|
||||
* Container类的异常捕获改进
|
||||
* Domain对象支持`bind`方法
|
||||
* 修正分页参数
|
||||
* 默认模板的输出规则不受URL影响
|
||||
* 注解路由支持多级控制器
|
||||
* Query类增加`getNumRows`方法获取前次操作影响的记录数
|
||||
* 改进查询条件的性能
|
||||
* 改进模型类`readTransform`方法对序列化类型的处理
|
||||
* Log类增加`close`方法可以临时关闭当前请求的日志写入
|
||||
* 文件日志方式增加自动清理功能(设置`max_files`参数)
|
||||
* 修正Query类的`getPk`方法
|
||||
* 修正模板缓存的布局开关问题
|
||||
* 修正Query类`select`方法的缓存
|
||||
* 改进input助手函数
|
||||
* 改进断线重连的信息判断
|
||||
* 改进正则验证方法
|
||||
* 调整语言包的加载顺序 放到`app_init`之前
|
||||
* controller类`fetch`方法改为`final`
|
||||
* 路由地址中的变量支持使用`<var>`方式
|
||||
* 改进XMLResponse 支持传入编码过的xml内容
|
||||
* 修正Query类`view`方法的数组表名支持
|
||||
* 改进路由的模型闭包绑定
|
||||
* 改进分组变量规则的继承
|
||||
* 改进`cli-server`模式下的`composer`自动加载
|
||||
* 路由变量规则异常捕获
|
||||
* 引入中间件支持
|
||||
* 路由定义增加`middleware`方法
|
||||
* 增加生成中间件指令`make:middleware`
|
||||
* 增加全局中间件定义支持
|
||||
* 改进`optimize:config`指令对全局中间件的支持
|
||||
* 改进config类`has`方法
|
||||
* 改进时间查询的参数绑定
|
||||
* 改进`inc/dec/exp`查询的安全性
|
||||
|
||||
|
||||
## V5.1.5 (2018-1-31)
|
||||
|
||||
该版本主要增强了数据库的JSON查询,并支持JSON字段的聚合查询,改进了一些性能问题,修正了路由的一些BUG,主要更新如下:
|
||||
|
||||
* 改进数据集查询对`JSON`数据的支持
|
||||
* 改进聚合查询对`JSON`字段的支持
|
||||
* 模型类增加`getOrFail`方法
|
||||
* 改进数据库驱动的`parseKey`方法
|
||||
* 改进Query类`join`方法的自关联查询
|
||||
* 改进数据查询不存在不生成查询缓存
|
||||
* 增加`run`命令行指令启动内置服务器
|
||||
* `Request`类`pathinfo`方法改进对`cli-server`支持
|
||||
* `Session`类增加`use_lock`配置参数设置是否启用锁机制
|
||||
* 优化`File`缓存自动生成空目录的问题
|
||||
* 域名及分组路由支持`append`方法传递隐式参数
|
||||
* 改进日志的并发写入问题
|
||||
* 改进`Query`类的`where`方法支持传入`Query`对象
|
||||
* 支持设置单个日志文件的文件名
|
||||
* 修正路由规则的域名条件约束
|
||||
* `Request`类增加`subDomain`方法用于获取当前子域名
|
||||
* `Response`类增加`allowCache`方法控制是否允许请求缓存
|
||||
* `Request`类增加`sendData`方法便于扩展
|
||||
* 改进`Env`类不依赖`putenv`方法
|
||||
* 改进控制台`trace`显示错误
|
||||
* 改进`MorphTo`关联
|
||||
* 改进完整路由匹配后带斜线访问出错的情况
|
||||
* 改进路由的多级分组问题
|
||||
* 路由url地址生成支持多级分组
|
||||
* 改进路由Url生成的`url_convert`参数的影响
|
||||
* 改进`miss`和`auto`路由内部解析
|
||||
* 取消预载入关联查询缓存功能
|
||||
|
||||
## V5.1.4 (2018-1-19)
|
||||
|
||||
该版本主要增强了数据库和模型操作,主要更新如下:
|
||||
|
||||
* 支持设置 `deleteTime`属性为`false` 关闭软删除
|
||||
* 模型增加`getError`方法
|
||||
* 改进Query类的`getTableFields`/`getFieldsType`方法 支持表名自动获取
|
||||
* 模型类`toCollection`方法增加参数指定数据集类
|
||||
* 改进`union`查询
|
||||
* 关联预载入`with`方法增加缓存参数
|
||||
* 改进模型类的`get`和`all`方法的缓存 支持关联缓存
|
||||
* 支持`order by field`操作
|
||||
* 改进`insertAll`分批写入
|
||||
* 改进`json`字段数据支持
|
||||
* 增加JSON数据的模型对象化操作
|
||||
* 改进路由`ext`参数检测
|
||||
* 修正`rule`方法的`method`参数使用 `get|post` 方式注册路由的问题
|
||||
|
||||
## V5.1.3 (2018-1-12)
|
||||
|
||||
该版本主要改进了路由及调整函数加载顺序,主要更新如下:
|
||||
|
||||
* 增加`env`助手函数;
|
||||
* 增加`route`助手函数;
|
||||
* 增加视图路由方法;
|
||||
* 增加路由重定向方法;
|
||||
* 路由默认区分最后的目录斜杆(支持设置不区分);
|
||||
* 调整公共文件和配置文件的加载顺序(可以在配置文件中直接使用助手函数);
|
||||
* 视图类增加`filter`方法设置输出过滤;
|
||||
* `view`助手函数增加`filter`参数;
|
||||
* 改进缓存生成指令;
|
||||
* Session类的`get`方法支持获取多级;
|
||||
* Request类`only`方法支持指定默认值;
|
||||
* 改进路由分组;
|
||||
* 修正使用闭包查询的时候自动数据缓存出错的情况;
|
||||
* 废除`view_filter`钩子位置;
|
||||
* 修正分组下面的资源路由;
|
||||
* 改进session驱动;
|
||||
|
||||
## V5.1.2 (2018-1-8)
|
||||
|
||||
该版本改进了配置类及数据库类,主要更新如下:
|
||||
|
||||
* 修正嵌套路由分组;
|
||||
* 修正自定义模板标签界定符后表达式语法出错的情况;
|
||||
* 修正自关联的多次调用问题;
|
||||
* 修正数组查询的`null`条件查询;
|
||||
* 修正Query类的`order`及`field`的一处可能的BUG;
|
||||
* 配置参数设置支持三级;
|
||||
* 配置对象支持`ArrayAccess`;
|
||||
* App类增加`path`方法用于设置应用目录;
|
||||
* 关联定义增加`selfRelation`方法用于设置是否为自关联;
|
||||
|
||||
## V5.1.1 (2018-1-3)
|
||||
|
||||
修正一些反馈的BUG,包括:
|
||||
|
||||
* 修正Cookie类存取数组的问题
|
||||
* 修正Controller的`fetch`方法
|
||||
* 改进跨域请求
|
||||
* 修正`insertAll`方法
|
||||
* 修正`chunk`方法
|
||||
|
||||
## V5.1.0 (2018-1-1)
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 增加注解路由支持
|
||||
* 路由支持跨域请求设置
|
||||
* 增加`app_dispatch`钩子位置
|
||||
* 修正多对多关联的`detach`方法
|
||||
* 修正软删除的`destroy`方法
|
||||
* Cookie类`httponly`参数默认为false
|
||||
* 日志File驱动增加`single`参数配置记录同一个文件(不按日期生成)
|
||||
* 路由的`ext`和`denyExt`方法支持不传任何参数
|
||||
* 改进模型的`save`方法对`oracle`的支持
|
||||
* Query类的`insertall`方法支持配合`data`和`limit`方法
|
||||
* 增加`whereOr`动态查询支持
|
||||
* 日志的ip地址记录改进
|
||||
* 模型`saveAll`方法支持`isUpdate`方法
|
||||
* 改进`Pivot`模型的实例化操作
|
||||
* 改进Model类的`data`方法
|
||||
* 改进多对多中间表模型类
|
||||
* 模型增加`force`方法强制更新所有数据
|
||||
* Hook类支持设置入口方法名称
|
||||
* 改进验证类
|
||||
* 改进`hasWhere`查询的数据重复问题
|
||||
* 模型的`saveall`方法返回数据集对象
|
||||
* 改进File缓存的`clear`方法
|
||||
* 缓存添加统一的序列化机制
|
||||
* 改进泛三级域名的绑定
|
||||
* 改进泛域名的传值和取值
|
||||
* Request类增加`panDomain`方法
|
||||
* 改进废弃字段判断
|
||||
* App类增加`create`方法用于实例化应用类库
|
||||
* 容器类增加`has`方法
|
||||
* 改进多数据库切换连接
|
||||
* 改进断线重连的异常捕获
|
||||
* 改进模型类`buildQuery`方法
|
||||
* Query类增加`unionAll`方法
|
||||
* 关联统计功能增强(支持Sum/Max/Min/Avg)
|
||||
* 修正延迟写入
|
||||
* chunk方法支持复合主键
|
||||
* 改进JSON类型的写入
|
||||
* 改进Mysql的insertAll方法
|
||||
* Model类`save`方法改进复合主键包含自增的情况
|
||||
* 改进Query类`inc`和`dec`方法的关键字处理
|
||||
* File缓存inc和dec方法保持原来的有效期
|
||||
* 改进redis缓存的有效期判断
|
||||
* 增加checkRule方法用于单独数据的多个验证规则
|
||||
* 修正setDec方法的延迟写入
|
||||
* max和min方法增加force参数
|
||||
* 二级配置参数区分大小写
|
||||
* 改进join方法自关联的问题
|
||||
* 修正关联模型自定义表名的情况
|
||||
* Query类增加getFieldsType和getTableFields方法
|
||||
* 取消视图替换功能及view_replace_str配置参数
|
||||
* 改进域名绑定模块后的额外路由规则问题
|
||||
* 改进mysql的insertAll方法
|
||||
* 改进insertAll方法写入json字段数据的支持
|
||||
* 改进redis长连接多编号库的情况
|
||||
|
||||
## RC3版本(2017-11-6)
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 改进redis驱动的`get`方法
|
||||
* 修正Query类的`alias`方法
|
||||
* `File`类错误信息支持多语言
|
||||
* 修正路由的额外参数解析
|
||||
* 改进`whereTime`方法
|
||||
* 改进Model类`getAttr`方法
|
||||
* 改进App类的`controller`和`validate`方法支持多层
|
||||
* 改进`HasManyThrough`类
|
||||
* 修正软删除的`restore`方法
|
||||
* 改进`MorpthTo`关联
|
||||
* 改进数据库驱动类的`parseKey`方法
|
||||
* 增加`whereField`动态查询方法
|
||||
* 模型增加废弃字段功能
|
||||
* 改进路由的`after`行为检查和`before`行为机制
|
||||
* 改进路由分组的检查
|
||||
* 修正mysql的`json`字段查询
|
||||
* 取消Connection类的`quote`方法
|
||||
* 改进命令行的支持
|
||||
* 验证信息支持多语言
|
||||
* 修正路由模型绑定
|
||||
* 改进参数绑定类型对枚举类型的支持
|
||||
* 修正模板的`{$Think.version} `输出
|
||||
* 改进模板`date`函数解析
|
||||
* 改进`insertAll`方法支持分批执行
|
||||
* Request类`host`方法支持反向代理
|
||||
* 改进`JumpResponse`支持区分成功和错误模板
|
||||
* 改进开启类库后缀后的关联外键自动识别问题
|
||||
* 修正一对一关联的JOIN方式预载入查询问题
|
||||
* Query类增加`hidden`方法
|
||||
|
||||
## RC2版本(2017-10-17)
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 修正视图查询
|
||||
* 修正资源路由
|
||||
* 修正`HasMany`关联 修正`where`方法的闭包查询
|
||||
* 一对一关联绑定属性到父模型后 关联属性不再保留
|
||||
* 修正应用的命令行配置文件读取
|
||||
* 改进`Connection`类的`getCacheKey`方法
|
||||
* 改进文件上传的非法图像异常
|
||||
* 改进验证类的`unique`规则
|
||||
* Config类`get`方法支持获取一级配置
|
||||
* 修正count方法对`fetchSql`的支持
|
||||
* 修正mysql驱动对`socket`支持
|
||||
* 改进Connection类的`getRealSql`方法
|
||||
* 修正`view`助手函数
|
||||
* Query类增加`leftJoin` `rightJoin` 和 `fullJoin`方法
|
||||
* 改进app_namespace的获取
|
||||
* 改进`append`方法对一对一`bind`属性的支持
|
||||
* 改进关联的`saveall`方法的返回值
|
||||
* 路由标识设置异常修复
|
||||
* 改进Route类`rule`方法
|
||||
* 改进模型的`table`属性设置
|
||||
* 改进composer autofile的加载顺序
|
||||
* 改进`exception_handle`配置对闭包的支持
|
||||
* 改进app助手函数增加参数
|
||||
* 改进composer的加载路径判断
|
||||
* 修正路由组合变量的URL生成
|
||||
* 修正路由URL生成
|
||||
* 改进`whereTime`查询并支持扩展规则
|
||||
* File类的`move`方法第二个参数支持`false`
|
||||
* 改进Config类
|
||||
* 改进缓存类`remember`方法
|
||||
* 惯例配置文件调整 Url类当普通模式参数的时候不做`urlencode`处理
|
||||
* 取消`ROOT_PATH`和`APP_PATH`常量定义 如需更改应用目录 自己重新定义入口文件
|
||||
* 增加`app_debug`的`Env`获取
|
||||
* 修正泛域名绑定
|
||||
* 改进查询表达式的解析机制
|
||||
* mysql增加`regexp`查询表达式 支持正则查询
|
||||
* 改进查询表达式的异常判断
|
||||
* 改进model类的`destroy`方法
|
||||
* 改进Builder类 取消`parseValue`方法
|
||||
* 修正like查询的参数绑定问题
|
||||
* console和start文件移出核心纳入应用库
|
||||
* 改进Db类主键删除方法
|
||||
* 改进泛域名绑定模块
|
||||
* 取消`BIND_MODULE`常量 改为在入口文件使用`bind`方法设置
|
||||
* 改进数组查询
|
||||
* 改进模板渲染的异常处理
|
||||
* 改进控制器基类的架构方法参数
|
||||
* 改进Controller类的`success`和`error`方法
|
||||
* 改进对浏览器`JSON-Handle`插件的支持
|
||||
* 优化跳转模板的移动端显示
|
||||
* 修正模型查询的`chunk`方法对时间字段的支持
|
||||
* 改进trace驱动
|
||||
* Collection类增加`push`方法
|
||||
* 改进Redis Session驱动
|
||||
* 增加JumpResponse驱动
|
||||
|
||||
|
||||
## RC1(2017-9-8)
|
||||
|
||||
主要新特性为:
|
||||
|
||||
* 引入容器和Facade支持
|
||||
* 依赖注入完善和支持更多场景
|
||||
* 重构的(对象化)路由
|
||||
* 配置和路由目录独立
|
||||
* 取消系统常量
|
||||
* 助手函数增强
|
||||
* 类库别名机制
|
||||
* 模型和数据库增强
|
||||
* 验证类增强
|
||||
* 模板引擎改进
|
||||
* 支持PSR-3日志规范
|
||||
* RC1版本取消了5.0多个字段批量数组查询的方式
|
||||
180
Server/README.md
180
Server/README.md
@@ -1,180 +0,0 @@
|
||||

|
||||
|
||||
ThinkPHP 5.1(LTS版本) —— 12载初心,你值得信赖的PHP框架
|
||||
===============
|
||||
|
||||
[](https://scrutinizer-ci.com/g/top-think/framework/?branch=5.1)
|
||||
[](https://travis-ci.org/top-think/framework)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
[](http://www.php.net/)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
|
||||
ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括:
|
||||
|
||||
+ 采用容器统一管理对象
|
||||
+ 支持Facade
|
||||
+ 注解路由支持
|
||||
+ 路由跨域请求支持
|
||||
+ 配置和路由目录独立
|
||||
+ 取消系统常量
|
||||
+ 助手函数增强
|
||||
+ 类库别名机制
|
||||
+ 增加条件查询
|
||||
+ 改进查询机制
|
||||
+ 配置采用二级
|
||||
+ 依赖注入完善
|
||||
+ 支持`PSR-3`日志规范
|
||||
+ 中间件支持(V5.1.6+)
|
||||
+ Swoole/Workerman支持(V5.1.18+)
|
||||
|
||||
|
||||
> ThinkPHP5的运行环境要求PHP5.6以上。
|
||||
|
||||
## 安装
|
||||
|
||||
使用composer安装
|
||||
|
||||
~~~
|
||||
composer create-project topthink/think tp
|
||||
~~~
|
||||
|
||||
启动服务
|
||||
|
||||
~~~
|
||||
cd tp
|
||||
php think run
|
||||
~~~
|
||||
|
||||
然后就可以在浏览器中访问
|
||||
|
||||
~~~
|
||||
http://localhost:8000
|
||||
~~~
|
||||
|
||||
更新框架
|
||||
~~~
|
||||
composer update topthink/framework
|
||||
~~~
|
||||
|
||||
|
||||
## 在线手册
|
||||
|
||||
+ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content)
|
||||
+ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155)
|
||||
|
||||
## 目录结构
|
||||
|
||||
初始的目录结构如下:
|
||||
|
||||
~~~
|
||||
www WEB部署目录(或者子目录)
|
||||
├─application 应用目录
|
||||
│ ├─common 公共模块目录(可以更改)
|
||||
│ ├─module_name 模块目录
|
||||
│ │ ├─common.php 模块函数文件
|
||||
│ │ ├─controller 控制器目录
|
||||
│ │ ├─model 模型目录
|
||||
│ │ ├─view 视图目录
|
||||
│ │ └─ ... 更多类库目录
|
||||
│ │
|
||||
│ ├─command.php 命令行定义文件
|
||||
│ ├─common.php 公共函数文件
|
||||
│ └─tags.php 应用行为扩展定义文件
|
||||
│
|
||||
├─config 应用配置目录
|
||||
│ ├─module_name 模块配置目录
|
||||
│ │ ├─database.php 数据库配置
|
||||
│ │ ├─cache 缓存配置
|
||||
│ │ └─ ...
|
||||
│ │
|
||||
│ ├─app.php 应用配置
|
||||
│ ├─cache.php 缓存配置
|
||||
│ ├─cookie.php Cookie配置
|
||||
│ ├─database.php 数据库配置
|
||||
│ ├─log.php 日志配置
|
||||
│ ├─session.php Session配置
|
||||
│ ├─template.php 模板引擎配置
|
||||
│ └─trace.php Trace配置
|
||||
│
|
||||
├─route 路由定义目录
|
||||
│ ├─route.php 路由定义
|
||||
│ └─... 更多
|
||||
│
|
||||
├─public WEB目录(对外访问目录)
|
||||
│ ├─index.php 入口文件
|
||||
│ ├─router.php 快速测试文件
|
||||
│ └─.htaccess 用于apache的重写
|
||||
│
|
||||
├─thinkphp 框架系统目录
|
||||
│ ├─lang 语言文件目录
|
||||
│ ├─library 框架类库目录
|
||||
│ │ ├─think Think类库包目录
|
||||
│ │ └─traits 系统Trait目录
|
||||
│ │
|
||||
│ ├─tpl 系统模板目录
|
||||
│ ├─base.php 基础定义文件
|
||||
│ ├─console.php 控制台入口文件
|
||||
│ ├─convention.php 框架惯例配置文件
|
||||
│ ├─helper.php 助手函数文件
|
||||
│ ├─phpunit.xml phpunit配置文件
|
||||
│ └─start.php 框架入口文件
|
||||
│
|
||||
├─extend 扩展类库目录
|
||||
├─runtime 应用的运行时目录(可写,可定制)
|
||||
├─vendor 第三方类库目录(Composer依赖库)
|
||||
├─build.php 自动生成定义文件(参考)
|
||||
├─composer.json composer 定义文件
|
||||
├─LICENSE.txt 授权说明文件
|
||||
├─README.md README 文件
|
||||
├─think 命令行入口文件
|
||||
~~~
|
||||
|
||||
> 可以使用php自带webserver快速测试
|
||||
> 切换到根目录后,启动命令:php think run
|
||||
|
||||
## 命名规范
|
||||
|
||||
`ThinkPHP5`遵循PSR-2命名规范和PSR-4自动加载规范,并且注意如下规范:
|
||||
|
||||
### 目录和文件
|
||||
|
||||
* 目录不强制规范,驼峰和小写+下划线模式均支持;
|
||||
* 类库、函数文件统一以`.php`为后缀;
|
||||
* 类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致;
|
||||
* 类名和类文件名保持一致,统一采用驼峰法命名(首字母大写);
|
||||
|
||||
### 函数和类、属性命名
|
||||
|
||||
* 类的命名采用驼峰法,并且首字母大写,例如 `User`、`UserType`,默认不需要添加后缀,例如`UserController`应该直接命名为`User`;
|
||||
* 函数的命名使用小写字母和下划线(小写字母开头)的方式,例如 `get_client_ip`;
|
||||
* 方法的命名使用驼峰法,并且首字母小写,例如 `getUserName`;
|
||||
* 属性的命名使用驼峰法,并且首字母小写,例如 `tableName`、`instance`;
|
||||
* 以双下划线“__”打头的函数或方法作为魔法方法,例如 `__call` 和 `__autoload`;
|
||||
|
||||
### 常量和配置
|
||||
|
||||
* 常量以大写字母和下划线命名,例如 `APP_PATH`和 `THINK_PATH`;
|
||||
* 配置参数以小写字母和下划线命名,例如 `url_route_on` 和`url_convert`;
|
||||
|
||||
### 数据表和字段
|
||||
|
||||
* 数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头,例如 `think_user` 表和 `user_name`字段,不建议使用驼峰和中文作为数据表字段命名。
|
||||
|
||||
## 参与开发
|
||||
|
||||
请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)。
|
||||
|
||||
## 版权信息
|
||||
|
||||
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
||||
|
||||
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
||||
|
||||
版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
|
||||
|
||||
All rights reserved。
|
||||
|
||||
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
||||
|
||||
更多细节参阅 [LICENSE.txt](LICENSE.txt)
|
||||
@@ -1,147 +0,0 @@
|
||||
# 微信朋友圈数据处理功能
|
||||
|
||||
本模块提供了微信朋友圈数据的获取、存储和查询功能,支持保留驼峰命名结构的原始数据。
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
项目包含一个数据表:
|
||||
|
||||
**wechat_moments** - 存储朋友圈基本信息
|
||||
- `id`: 自增主键
|
||||
- `wechatAccountId`: 微信账号ID
|
||||
- `wechatFriendId`: 微信好友ID
|
||||
- `snsId`: 朋友圈消息ID
|
||||
- `commentList`: 评论列表JSON
|
||||
- `createTime`: 创建时间戳
|
||||
- `likeList`: 点赞列表JSON
|
||||
- `content`: 朋友圈内容
|
||||
- `lat`: 纬度
|
||||
- `lng`: 经度
|
||||
- `location`: 位置信息
|
||||
- `picSize`: 图片大小
|
||||
- `resUrls`: 资源URL列表
|
||||
- `userName`: 用户名
|
||||
- `type`: 朋友圈类型
|
||||
- `create_time`: 数据创建时间
|
||||
- `update_time`: 数据更新时间
|
||||
|
||||
## API接口
|
||||
|
||||
### 1. 获取朋友圈信息
|
||||
|
||||
```
|
||||
GET/POST /api/websocket/getMoments
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `wechatAccountId`: 微信账号ID
|
||||
- `wechatFriendId`: 微信好友ID
|
||||
- `count`: 获取条数,默认5条
|
||||
|
||||
获取指定账号和好友的朋友圈信息,并自动保存到数据库。
|
||||
|
||||
### 2. 保存单条朋友圈数据
|
||||
|
||||
```
|
||||
POST /api/websocket/saveSingleMoment
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `commentList`: 评论列表
|
||||
- `createTime`: 创建时间戳
|
||||
- `likeList`: 点赞列表
|
||||
- `momentEntity`: 朋友圈实体,包含以下字段:
|
||||
- `content`: 朋友圈内容
|
||||
- `lat`: 纬度
|
||||
- `lng`: 经度
|
||||
- `location`: 位置信息
|
||||
- `picSize`: 图片大小
|
||||
- `resUrls`: 资源URL列表
|
||||
- `urls`: 媒体URL列表
|
||||
- `userName`: 用户名
|
||||
- `snsId`: 朋友圈ID
|
||||
- `type`: 朋友圈类型
|
||||
- `wechatAccountId`: 微信账号ID
|
||||
- `wechatFriendId`: 微信好友ID
|
||||
|
||||
保存单条朋友圈数据到数据库,保持原有的驼峰数据结构。系统会将`momentEntity`中的字段提取并单独存储,不包括`objectType`和`createTime`字段。
|
||||
|
||||
### 3. 获取朋友圈数据列表
|
||||
|
||||
```
|
||||
GET/POST /api/websocket/getMomentsList
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `wechatAccountId`: 微信账号ID (可选)
|
||||
- `wechatFriendId`: 微信好友ID (可选)
|
||||
- `page`: 页码,默认1
|
||||
- `pageSize`: 每页条数,默认10
|
||||
- `startTime`: 开始时间戳 (可选)
|
||||
- `endTime`: 结束时间戳 (可选)
|
||||
|
||||
获取已保存的朋友圈数据列表,支持分页和条件筛选。返回的数据会自动构建`momentEntity`字段以保持API兼容性。
|
||||
|
||||
### 4. 获取朋友圈详情
|
||||
|
||||
```
|
||||
GET/POST /api/websocket/getMomentDetail
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `snsId`: 朋友圈ID
|
||||
- `wechatAccountId`: 微信账号ID
|
||||
|
||||
获取单条朋友圈的详细信息,包括评论、点赞和资源URL等。返回的数据会自动构建`momentEntity`字段以保持API兼容性。
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 保存单条朋友圈数据
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'commentList' => [],
|
||||
'createTime' => 1742777232,
|
||||
'likeList' => [],
|
||||
'momentEntity' => [
|
||||
'content' => "第一位个人与Stussy联名的中国名人,不是陈冠希,不是葛民辉,而是周杰伦!",
|
||||
'lat' => 0,
|
||||
'lng' => 0,
|
||||
'location' => "",
|
||||
'picSize' => 0,
|
||||
'resUrls' => [],
|
||||
'snsId' => "-3827269039168736643",
|
||||
'urls' => ["http://wxapp.tc.qq.com/251/20304/stodownload?encfilekey=..."],
|
||||
'userName' => "wxid_afixeeh53lt012"
|
||||
],
|
||||
'snsId' => "-3827269039168736643",
|
||||
'type' => 28,
|
||||
'wechatAccountId' => 123456, // 替换为实际的微信账号ID
|
||||
'wechatFriendId' => "wxid_example" // 替换为实际的微信好友ID
|
||||
];
|
||||
|
||||
// 发送请求
|
||||
$result = curl_post('/api/websocket/saveSingleMoment', $data);
|
||||
```
|
||||
|
||||
### 查询朋友圈列表
|
||||
|
||||
```php
|
||||
// 获取特定账号的朋友圈
|
||||
$params = [
|
||||
'wechatAccountId' => 123456,
|
||||
'page' => 1,
|
||||
'pageSize' => 20
|
||||
];
|
||||
|
||||
// 发送请求
|
||||
$result = curl_get('/api/websocket/getMomentsList', $params);
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 所有JSON格式的数据在保存时都会进行编码,查询时会自动解码并还原为原始数据结构。
|
||||
2. 数据库中的字段名保持驼峰命名格式,与微信API返回的数据结构保持一致。
|
||||
3. 尽管数据库中将`momentEntity`的字段拆分为独立字段存储,但API接口返回时会重新构建`momentEntity`结构,以保持与原始API的兼容性。
|
||||
4. `objectType`和`createTime`字段已从`momentEntity`中移除,不再单独存储。
|
||||
5. 图片或视频资源URLs直接存储在朋友圈主表中,不再单独存储到资源表。
|
||||
@@ -1,105 +0,0 @@
|
||||
# 微信群聊同步功能
|
||||
|
||||
本功能用于自动同步微信群聊数据,支持分页获取群聊列表以及群成员信息,并将数据保存到数据库中。
|
||||
|
||||
## 功能特点
|
||||
|
||||
1. 支持分页获取微信群聊列表
|
||||
2. 自动获取每个群聊的成员信息
|
||||
3. 支持通过关键词筛选群聊
|
||||
4. 支持按微信账号筛选群聊
|
||||
5. 可选择是否包含已删除的群聊
|
||||
6. 使用队列处理,支持大量数据的同步
|
||||
7. 支持失败重试机制
|
||||
8. 提供命令行和HTTP接口两种触发方式
|
||||
|
||||
## 数据表结构
|
||||
|
||||
本功能使用以下数据表:
|
||||
|
||||
1. **wechat_chatroom** - 存储微信群聊信息
|
||||
2. **wechat_chatroom_member** - 存储微信群聊成员信息
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. HTTP接口触发
|
||||
|
||||
```
|
||||
GET/POST /api/wechat_chatroom/syncChatrooms
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `pageIndex`: 起始页码,默认0
|
||||
- `pageSize`: 每页大小,默认100
|
||||
- `keyword`: 群名关键词,可选
|
||||
- `wechatAccountKeyword`: 微信账号关键词,可选
|
||||
- `isDeleted`: 是否包含已删除群聊,可选
|
||||
|
||||
**示例:**
|
||||
```
|
||||
/api/wechat_chatroom/syncChatrooms?pageSize=50
|
||||
```
|
||||
|
||||
### 2. 命令行触发
|
||||
|
||||
```bash
|
||||
php think sync:wechat:chatrooms [选项]
|
||||
```
|
||||
|
||||
**选项:**
|
||||
- `-p, --pageIndex`: 起始页码,默认0
|
||||
- `-s, --pageSize`: 每页大小,默认100
|
||||
- `-k, --keyword`: 群名关键词,可选
|
||||
- `-a, --account`: 微信账号关键词,可选
|
||||
- `-d, --deleted`: 是否包含已删除群聊,可选
|
||||
|
||||
**示例:**
|
||||
```bash
|
||||
# 基本用法
|
||||
php think sync:wechat:chatrooms
|
||||
|
||||
# 指定页大小和关键词
|
||||
php think sync:wechat:chatrooms -s 50 -k "测试群"
|
||||
|
||||
# 指定账号关键词
|
||||
php think sync:wechat:chatrooms --account "张三"
|
||||
```
|
||||
|
||||
### 3. 定时任务配置
|
||||
|
||||
可以将命令添加到系统的定时任务(crontab)中,实现定期自动同步:
|
||||
|
||||
```
|
||||
# 每天凌晨3点执行微信群聊同步
|
||||
0 3 * * * cd /path/to/your/project && php think sync:wechat:chatrooms
|
||||
```
|
||||
|
||||
## 队列消费者配置
|
||||
|
||||
为了处理同步任务,需要启动队列消费者:
|
||||
|
||||
```bash
|
||||
# 启动微信群聊队列消费者
|
||||
php think queue:work --queue wechat_chatrooms
|
||||
```
|
||||
|
||||
建议在生产环境中使用supervisor等工具来管理队列消费者进程。
|
||||
|
||||
## 同步过程
|
||||
|
||||
1. 触发同步任务,将初始页任务加入队列
|
||||
2. 队列消费者处理任务,获取当前页的群聊列表
|
||||
3. 如果当前页有数据且数量等于页大小,则将下一页任务加入队列
|
||||
4. 对每个获取到的群聊,添加获取群成员的任务
|
||||
5. 所有数据会自动保存到数据库中
|
||||
|
||||
## 调试与日志
|
||||
|
||||
同步过程的日志会记录在应用的日志目录中,可以通过查看日志了解同步状态和错误信息。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 页大小建议设置为合理值(50-100),过大会导致请求超时
|
||||
2. 当数据量较大时,建议增加队列消费者的数量
|
||||
3. 确保系统授权信息正确,否则无法获取数据
|
||||
4. 数据同步是增量的,会自动更新已存在的记录
|
||||
@@ -23,6 +23,7 @@ Route::group('v1/', function () {
|
||||
|
||||
// 设备微信相关
|
||||
Route::group('device/wechats', function () {
|
||||
Route::get('friends', 'app\\devices\\controller\\DeviceWechat@getFriends'); // 获取微信好友列表
|
||||
Route::get('count', 'app\\devices\\controller\\DeviceWechat@count'); // 获取在线微信账号数量
|
||||
Route::get('device-count', 'app\\devices\\controller\\DeviceWechat@deviceCount'); // 获取有登录微信的设备数量
|
||||
Route::get('', 'app\\devices\\controller\\DeviceWechat@index'); // 获取在线微信账号列表
|
||||
|
||||
@@ -274,23 +274,6 @@ class DeviceWechat extends Controller
|
||||
]
|
||||
];
|
||||
|
||||
// 获取微信好友列表
|
||||
$friends = Db::table('tk_wechat_friend')
|
||||
->where('wechatAccountId', $id)
|
||||
->where('isDeleted', 0)
|
||||
->field([
|
||||
'id',
|
||||
'wechatId',
|
||||
'nickname',
|
||||
'avatar',
|
||||
'gender',
|
||||
'region',
|
||||
'signature',
|
||||
'labels',
|
||||
'createTime'
|
||||
])
|
||||
->select();
|
||||
|
||||
// 处理返回数据
|
||||
$data = [
|
||||
'basicInfo' => [
|
||||
@@ -322,7 +305,6 @@ class DeviceWechat extends Controller
|
||||
'lastUpdateTime' => $wechat['updateTime']
|
||||
],
|
||||
'restrictions' => $restrictions,
|
||||
'friends' => $friends
|
||||
];
|
||||
|
||||
return json([
|
||||
@@ -528,4 +510,73 @@ class DeviceWechat extends Controller
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信好友列表
|
||||
* 根据wechatId查询微信好友,支持分页和关键词筛选
|
||||
*
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getFriends()
|
||||
{
|
||||
try {
|
||||
// 获取请求参数
|
||||
$wechatId = Request::param('wechatId');
|
||||
$page = (int)Request::param('page', 1);
|
||||
$limit = (int)Request::param('limit', 20);
|
||||
$keyword = Request::param('keyword', '');
|
||||
|
||||
// 参数验证
|
||||
if (empty($wechatId)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '参数错误:微信ID不能为空'
|
||||
]);
|
||||
}
|
||||
|
||||
// 查询参数
|
||||
$params = [];
|
||||
if (!empty($keyword)) {
|
||||
$params['keyword'] = $keyword;
|
||||
}
|
||||
|
||||
// 调用模型方法获取好友列表
|
||||
$result = \app\devices\model\WechatFriend::getFriendsByWechatId($wechatId, $params, $page, $limit);
|
||||
|
||||
|
||||
|
||||
// 处理返回的数据
|
||||
$friendsList = [];
|
||||
foreach ($result['list'] as $friend) {
|
||||
$friendsList[] = [
|
||||
'wechatId' => $friend['wechatId'],
|
||||
'avatar' => $friend['avatar'] ?: '/placeholder.svg',
|
||||
'labels' => $friend['labels'] ?: [],
|
||||
'accountNickname' => $friend['accountNickname'] ?: '',
|
||||
'accountRealName' => $friend['accountRealName'] ?: '',
|
||||
'nickname' => $friend['nickname'] ?: '',
|
||||
'remark' => $friend['conRemark'] ?: '',
|
||||
'alias' => $friend['alias'] ?: '',
|
||||
'gender' => $friend['gender'] ?: 0,
|
||||
'region' => $friend['region'] ?: ''
|
||||
];
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'total' => $result['total'],
|
||||
'page' => $result['page'],
|
||||
'limit' => $result['limit'],
|
||||
'list' => $friendsList
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '获取失败:' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Server/application/devices/model/WechatFriend.php
Normal file
84
Server/application/devices/model/WechatFriend.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace app\devices\model;
|
||||
|
||||
use think\Model;
|
||||
use think\Db;
|
||||
|
||||
/**
|
||||
* 微信好友模型类
|
||||
*/
|
||||
class WechatFriend extends Model
|
||||
{
|
||||
// 设置表名
|
||||
protected $name = 'wechat_friend';
|
||||
protected $prefix = 'tk_';
|
||||
|
||||
// 设置主键
|
||||
protected $pk = 'id';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = 'datetime';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createTime';
|
||||
protected $updateTime = 'updateTime';
|
||||
|
||||
// 定义字段类型
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'wechatAccountId' => 'integer',
|
||||
'gender' => 'integer',
|
||||
'addFrom' => 'integer',
|
||||
'isDeleted' => 'integer',
|
||||
'isPassed' => 'integer',
|
||||
'accountId' => 'integer',
|
||||
'groupId' => 'integer',
|
||||
'labels' => 'json',
|
||||
'deleteTime' => 'datetime',
|
||||
'passTime' => 'datetime',
|
||||
'createTime' => 'datetime'
|
||||
];
|
||||
|
||||
/**
|
||||
* 根据微信账号ID获取好友列表
|
||||
*
|
||||
* @param string $ownerWechatId 所有者微信ID
|
||||
* @param array $params 查询条件参数
|
||||
* @param int $page 页码
|
||||
* @param int $limit 每页数量
|
||||
* @return array 好友列表和总数
|
||||
*/
|
||||
public static function getFriendsByWechatId($ownerWechatId, $params = [], $page = 1, $limit = 20)
|
||||
{
|
||||
// 构建基础查询
|
||||
$query = self::where('ownerWechatId', $ownerWechatId)
|
||||
->where('isDeleted', 0);
|
||||
|
||||
// 添加筛选条件(昵称、备注、微信号、标签)
|
||||
if (!empty($params['keyword'])) {
|
||||
$keyword = $params['keyword'];
|
||||
$query->where(function($q) use ($keyword) {
|
||||
$q->whereOr('nickname', 'like', "%{$keyword}%")
|
||||
->whereOr('conRemark', 'like', "%{$keyword}%")
|
||||
->whereOr('alias', 'like', "%{$keyword}%")
|
||||
->whereOr("JSON_SEARCH(labels, 'one', '%{$keyword}%') IS NOT NULL");
|
||||
});
|
||||
}
|
||||
|
||||
// 计算总数
|
||||
$total = $query->count();
|
||||
|
||||
// 分页查询数据
|
||||
$friends = $query->page($page, $limit)
|
||||
->order('createTime desc')
|
||||
->field('wechatId, alias, avatar, labels, accountNickname, accountRealName, nickname, conRemark, gender, region')
|
||||
->select();
|
||||
|
||||
return [
|
||||
'list' => $friends,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit
|
||||
];
|
||||
}
|
||||
}
|
||||
7
Server/thinkphp/.gitignore
vendored
Normal file
7
Server/thinkphp/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
/phpunit.xml
|
||||
/.idea
|
||||
/.vscode
|
||||
1
Server/thinkphp/.htaccess
Normal file
1
Server/thinkphp/.htaccess
Normal file
@@ -0,0 +1 @@
|
||||
deny from all
|
||||
119
Server/thinkphp/CONTRIBUTING.md
Normal file
119
Server/thinkphp/CONTRIBUTING.md
Normal file
@@ -0,0 +1,119 @@
|
||||
如何贡献我的源代码
|
||||
===
|
||||
|
||||
此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。
|
||||
|
||||
## 通过 Github 贡献代码
|
||||
|
||||
ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
|
||||
|
||||
参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请并。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
|
||||
|
||||
我们希望你贡献的代码符合:
|
||||
|
||||
* ThinkPHP 的编码规范
|
||||
* 适当的注释,能让其他人读懂
|
||||
* 遵循 Apache2 开源协议
|
||||
|
||||
**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容**
|
||||
|
||||
### 注意事项
|
||||
|
||||
* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141);
|
||||
* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144);
|
||||
* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。
|
||||
* 系统会自动在 PHP 5.4 5.5 5.6 7.0 和 HHVM 上测试修改,其中 HHVM 下的测试容许报错,请确保你的修改符合 PHP 5.4 ~ 5.6 和 PHP 7.0 的语法规范;
|
||||
* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests);
|
||||
|
||||
## GitHub Issue
|
||||
|
||||
GitHub 提供了 Issue 功能,该功能可以用于:
|
||||
|
||||
* 提出 bug
|
||||
* 提出功能改进
|
||||
* 反馈使用体验
|
||||
|
||||
该功能不应该用于:
|
||||
|
||||
* 提出修改意见(涉及代码署名和修订追溯问题)
|
||||
* 不友善的言论
|
||||
|
||||
## 快速修改
|
||||
|
||||
**GitHub 提供了快速编辑文件的功能**
|
||||
|
||||
1. 登录 GitHub 帐号;
|
||||
2. 浏览项目文件,找到要进行修改的文件;
|
||||
3. 点击右上角铅笔图标进行修改;
|
||||
4. 填写 `Commit changes` 相关内容(Title 必填);
|
||||
5. 提交修改,等待 CI 验证和管理员合并。
|
||||
|
||||
**若您需要一次提交大量修改,请继续阅读下面的内容**
|
||||
|
||||
## 完整流程
|
||||
|
||||
1. `fork`本项目;
|
||||
2. 克隆(`clone`)你 `fork` 的项目到本地;
|
||||
3. 新建分支(`branch`)并检出(`checkout`)新分支;
|
||||
4. 添加本项目到你的本地 git 仓库作为上游(`upstream`);
|
||||
5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests);
|
||||
6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
|
||||
7. `push` 你的本地仓库到 GitHub;
|
||||
8. 提交 `pull request`;
|
||||
9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`);
|
||||
10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
|
||||
|
||||
*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*
|
||||
|
||||
*绝对不可以使用 `git push -f` 强行推送修改到上游*
|
||||
|
||||
### 注意事项
|
||||
|
||||
* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/);
|
||||
* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分);
|
||||
* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/)
|
||||
|
||||
## 推荐资源
|
||||
|
||||
### 开发环境
|
||||
|
||||
* XAMPP for Windows 5.5.x
|
||||
* WampServer (for Windows)
|
||||
* upupw Apache PHP5.4 ( for Windows)
|
||||
|
||||
或自行安装
|
||||
|
||||
- Apache / Nginx
|
||||
- PHP 5.4 ~ 5.6
|
||||
- MySQL / MariaDB
|
||||
|
||||
*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer*
|
||||
|
||||
*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB*
|
||||
|
||||
### 编辑器
|
||||
|
||||
Sublime Text 3 + phpfmt 插件
|
||||
|
||||
phpfmt 插件参数
|
||||
|
||||
```json
|
||||
{
|
||||
"autocomplete": true,
|
||||
"enable_auto_align": true,
|
||||
"format_on_save": true,
|
||||
"indent_with_space": true,
|
||||
"psr1_naming": false,
|
||||
"psr2": true,
|
||||
"version": 4
|
||||
}
|
||||
```
|
||||
|
||||
或其他 编辑器 / IDE 配合 PSR2 自动格式化工具
|
||||
|
||||
### Git GUI
|
||||
|
||||
* SourceTree
|
||||
* GitHub Desktop
|
||||
|
||||
或其他 Git 图形界面客户端
|
||||
0
Server/LICENSE.txt → Server/thinkphp/LICENSE.txt
Executable file → Normal file
0
Server/LICENSE.txt → Server/thinkphp/LICENSE.txt
Executable file → Normal file
99
Server/thinkphp/README.md
Normal file
99
Server/thinkphp/README.md
Normal file
@@ -0,0 +1,99 @@
|
||||

|
||||
|
||||
ThinkPHP 5.1(LTS) —— 12载初心,你值得信赖的PHP框架
|
||||
===============
|
||||
|
||||
[](https://scrutinizer-ci.com/g/top-think/framework/?branch=5.1)
|
||||
[](https://travis-ci.org/top-think/framework)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
[](http://www.php.net/)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
|
||||
ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括:
|
||||
|
||||
+ 采用容器统一管理对象
|
||||
+ 支持Facade
|
||||
+ 更易用的路由
|
||||
+ 注解路由支持
|
||||
+ 路由跨域请求支持
|
||||
+ 验证类增强
|
||||
+ 配置和路由目录独立
|
||||
+ 取消系统常量
|
||||
+ 类库别名机制
|
||||
+ 模型和数据库增强
|
||||
+ 依赖注入完善
|
||||
+ 支持PSR-3日志规范
|
||||
+ 中间件支持(`V5.1.6+`)
|
||||
+ 支持`Swoole`/`Workerman`运行(`V5.1.18+`)
|
||||
|
||||
官方已经正式宣布`5.1.27`版本为LTS版本。
|
||||
|
||||
### 废除的功能:
|
||||
|
||||
+ 聚合模型
|
||||
+ 内置控制器扩展类
|
||||
+ 模型自动验证
|
||||
|
||||
> ThinkPHP5.1的运行环境要求PHP5.6+ 兼容PHP8.0。
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
使用composer安装
|
||||
|
||||
~~~
|
||||
composer create-project topthink/think tp
|
||||
~~~
|
||||
|
||||
启动服务
|
||||
|
||||
~~~
|
||||
cd tp
|
||||
php think run
|
||||
~~~
|
||||
|
||||
然后就可以在浏览器中访问
|
||||
|
||||
~~~
|
||||
http://localhost:8000
|
||||
~~~
|
||||
|
||||
更新框架
|
||||
~~~
|
||||
composer update topthink/framework
|
||||
~~~
|
||||
|
||||
|
||||
## 在线手册
|
||||
|
||||
+ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content)
|
||||
+ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155)
|
||||
|
||||
|
||||
## 官方服务
|
||||
|
||||
+ [应用服务市场](https://market.topthink.com/)
|
||||
+ [ThinkAPI——统一API服务](https://docs.topthink.com/think-api)
|
||||
|
||||
## 命名规范
|
||||
|
||||
`ThinkPHP5.1`遵循PSR-2命名规范和PSR-4自动加载规范。
|
||||
|
||||
## 参与开发
|
||||
|
||||
请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)。
|
||||
|
||||
## 版权信息
|
||||
|
||||
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
||||
|
||||
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
||||
|
||||
版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
|
||||
|
||||
All rights reserved。
|
||||
|
||||
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
||||
|
||||
更多细节参阅 [LICENSE.txt](LICENSE.txt)
|
||||
52
Server/thinkphp/base.php
Normal file
52
Server/thinkphp/base.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think;
|
||||
|
||||
// 载入Loader类
|
||||
require __DIR__ . '/library/think/Loader.php';
|
||||
|
||||
// 注册自动加载
|
||||
Loader::register();
|
||||
|
||||
// 注册错误和异常处理机制
|
||||
Error::register();
|
||||
|
||||
// 实现日志接口
|
||||
if (interface_exists('Psr\Log\LoggerInterface')) {
|
||||
interface LoggerInterface extends \Psr\Log\LoggerInterface
|
||||
{}
|
||||
} else {
|
||||
interface LoggerInterface
|
||||
{}
|
||||
}
|
||||
|
||||
// 注册类库别名
|
||||
Loader::addClassAlias([
|
||||
'App' => facade\App::class,
|
||||
'Build' => facade\Build::class,
|
||||
'Cache' => facade\Cache::class,
|
||||
'Config' => facade\Config::class,
|
||||
'Cookie' => facade\Cookie::class,
|
||||
'Db' => Db::class,
|
||||
'Debug' => facade\Debug::class,
|
||||
'Env' => facade\Env::class,
|
||||
'Facade' => Facade::class,
|
||||
'Hook' => facade\Hook::class,
|
||||
'Lang' => facade\Lang::class,
|
||||
'Log' => facade\Log::class,
|
||||
'Request' => facade\Request::class,
|
||||
'Response' => facade\Response::class,
|
||||
'Route' => facade\Route::class,
|
||||
'Session' => facade\Session::class,
|
||||
'Url' => facade\Url::class,
|
||||
'Validate' => facade\Validate::class,
|
||||
'View' => facade\View::class,
|
||||
]);
|
||||
35
Server/thinkphp/composer.json
Normal file
35
Server/thinkphp/composer.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "topthink/framework",
|
||||
"description": "the new thinkphp framework",
|
||||
"type": "think-framework",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"thinkphp",
|
||||
"ORM"
|
||||
],
|
||||
"homepage": "http://thinkphp.cn/",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "liu21st",
|
||||
"email": "liu21st@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"topthink/think-installer": "2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.0|^6.0",
|
||||
"johnkary/phpunit-speedtrap": "^1.0",
|
||||
"mikey179/vfsstream": "~1.6",
|
||||
"phploc/phploc": "2.*",
|
||||
"sebastian/phpcpd": "2.*",
|
||||
"squizlabs/php_codesniffer": "2.*",
|
||||
"phpdocumentor/reflection-docblock": "^2.0"
|
||||
}
|
||||
}
|
||||
327
Server/thinkphp/convention.php
Normal file
327
Server/thinkphp/convention.php
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// +----------------------------------------------------------------------
|
||||
// | 应用设置
|
||||
// +----------------------------------------------------------------------
|
||||
'app' => [
|
||||
// 应用名称
|
||||
'app_name' => '',
|
||||
// 应用地址
|
||||
'app_host' => '',
|
||||
// 应用调试模式
|
||||
'app_debug' => false,
|
||||
// 应用Trace
|
||||
'app_trace' => false,
|
||||
// 应用模式状态
|
||||
'app_status' => '',
|
||||
// 是否HTTPS
|
||||
'is_https' => false,
|
||||
// 入口自动绑定模块
|
||||
'auto_bind_module' => false,
|
||||
// 注册的根命名空间
|
||||
'root_namespace' => [],
|
||||
// 默认输出类型
|
||||
'default_return_type' => 'html',
|
||||
// 默认AJAX 数据返回格式,可选json xml ...
|
||||
'default_ajax_return' => 'json',
|
||||
// 默认JSONP格式返回的处理方法
|
||||
'default_jsonp_handler' => 'jsonpReturn',
|
||||
// 默认JSONP处理方法
|
||||
'var_jsonp_handler' => 'callback',
|
||||
// 默认时区
|
||||
'default_timezone' => 'Asia/Shanghai',
|
||||
// 是否开启多语言
|
||||
'lang_switch_on' => false,
|
||||
// 默认验证器
|
||||
'default_validate' => '',
|
||||
// 默认语言
|
||||
'default_lang' => 'zh-cn',
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 模块设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 自动搜索控制器
|
||||
'controller_auto_search' => false,
|
||||
// 操作方法前缀
|
||||
'use_action_prefix' => false,
|
||||
// 操作方法后缀
|
||||
'action_suffix' => '',
|
||||
// 默认的空控制器名
|
||||
'empty_controller' => 'Error',
|
||||
// 默认的空模块名
|
||||
'empty_module' => '',
|
||||
// 默认模块名
|
||||
'default_module' => 'index',
|
||||
// 是否支持多模块
|
||||
'app_multi_module' => true,
|
||||
// 禁止访问模块
|
||||
'deny_module_list' => ['common'],
|
||||
// 默认控制器名
|
||||
'default_controller' => 'Index',
|
||||
// 默认操作名
|
||||
'default_action' => 'index',
|
||||
// 是否自动转换URL中的控制器和操作名
|
||||
'url_convert' => true,
|
||||
// 默认的访问控制器层
|
||||
'url_controller_layer' => 'controller',
|
||||
// 应用类库后缀
|
||||
'class_suffix' => false,
|
||||
// 控制器类后缀
|
||||
'controller_suffix' => false,
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | URL请求设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 默认全局过滤方法 用逗号分隔多个
|
||||
'default_filter' => '',
|
||||
// PATHINFO变量名 用于兼容模式
|
||||
'var_pathinfo' => 's',
|
||||
// 兼容PATH_INFO获取
|
||||
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
|
||||
// HTTPS代理标识
|
||||
'https_agent_name' => '',
|
||||
// IP代理获取标识
|
||||
'http_agent_ip' => 'HTTP_X_REAL_IP',
|
||||
// URL伪静态后缀
|
||||
'url_html_suffix' => 'html',
|
||||
// 域名根,如thinkphp.cn
|
||||
'url_domain_root' => '',
|
||||
// 表单请求类型伪装变量
|
||||
'var_method' => '_method',
|
||||
// 表单ajax伪装变量
|
||||
'var_ajax' => '_ajax',
|
||||
// 表单pjax伪装变量
|
||||
'var_pjax' => '_pjax',
|
||||
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
|
||||
'request_cache' => false,
|
||||
// 请求缓存有效期
|
||||
'request_cache_expire' => null,
|
||||
// 全局请求缓存排除规则
|
||||
'request_cache_except' => [],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 路由设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// pathinfo分隔符
|
||||
'pathinfo_depr' => '/',
|
||||
// URL普通方式参数 用于自动生成
|
||||
'url_common_param' => false,
|
||||
// URL参数方式 0 按名称成对解析 1 按顺序解析
|
||||
'url_param_type' => 0,
|
||||
// 是否开启路由延迟解析
|
||||
'url_lazy_route' => false,
|
||||
// 是否强制使用路由
|
||||
'url_route_must' => false,
|
||||
// 合并路由规则
|
||||
'route_rule_merge' => false,
|
||||
// 路由是否完全匹配
|
||||
'route_complete_match' => false,
|
||||
// 使用注解路由
|
||||
'route_annotation' => false,
|
||||
// 默认的路由变量规则
|
||||
'default_route_pattern' => '\w+',
|
||||
// 是否开启路由缓存
|
||||
'route_check_cache' => false,
|
||||
// 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5
|
||||
'route_check_cache_key' => '',
|
||||
// 路由缓存的设置
|
||||
'route_cache_option' => [],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 异常及错误设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 默认跳转页面对应的模板文件
|
||||
'dispatch_success_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl',
|
||||
'dispatch_error_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl',
|
||||
// 异常页面的模板文件
|
||||
'exception_tmpl' => __DIR__ . '/tpl/think_exception.tpl',
|
||||
// 错误显示信息,非调试模式有效
|
||||
'error_message' => '页面错误!请稍后再试~',
|
||||
// 显示错误信息
|
||||
'show_error_msg' => false,
|
||||
// 异常处理handle类 留空使用 \think\exception\Handle
|
||||
'exception_handle' => '',
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 模板设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'template' => [
|
||||
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
|
||||
'auto_rule' => 1,
|
||||
// 模板引擎类型 支持 php think 支持扩展
|
||||
'type' => 'Think',
|
||||
// 视图基础目录,配置目录为所有模块的视图起始目录
|
||||
'view_base' => '',
|
||||
// 当前模板的视图目录 留空为自动获取
|
||||
'view_path' => '',
|
||||
// 模板后缀
|
||||
'view_suffix' => 'html',
|
||||
// 模板文件名分隔符
|
||||
'view_depr' => DIRECTORY_SEPARATOR,
|
||||
// 模板引擎普通标签开始标记
|
||||
'tpl_begin' => '{',
|
||||
// 模板引擎普通标签结束标记
|
||||
'tpl_end' => '}',
|
||||
// 标签库标签开始标记
|
||||
'taglib_begin' => '{',
|
||||
// 标签库标签结束标记
|
||||
'taglib_end' => '}',
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 日志设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'log' => [
|
||||
// 日志记录方式,内置 file socket 支持扩展
|
||||
'type' => 'File',
|
||||
// 日志保存目录
|
||||
//'path' => LOG_PATH,
|
||||
// 日志记录级别
|
||||
'level' => [],
|
||||
// 是否记录trace信息到日志
|
||||
'record_trace' => false,
|
||||
// 是否JSON格式记录
|
||||
'json' => false,
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Trace设置 开启 app_trace 后 有效
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'trace' => [
|
||||
// 内置Html Console 支持扩展
|
||||
'type' => 'Html',
|
||||
'file' => __DIR__ . '/tpl/page_trace.tpl',
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 缓存设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'cache' => [
|
||||
// 驱动方式
|
||||
'type' => 'File',
|
||||
// 缓存保存目录
|
||||
//'path' => CACHE_PATH,
|
||||
// 缓存前缀
|
||||
'prefix' => '',
|
||||
// 缓存有效期 0表示永久缓存
|
||||
'expire' => 0,
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 会话设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'session' => [
|
||||
'id' => '',
|
||||
// SESSION_ID的提交变量,解决flash上传跨域
|
||||
'var_session_id' => '',
|
||||
// SESSION 前缀
|
||||
'prefix' => 'think',
|
||||
// 驱动方式 支持redis memcache memcached
|
||||
'type' => '',
|
||||
// 是否自动开启 SESSION
|
||||
'auto_start' => true,
|
||||
'httponly' => true,
|
||||
'secure' => false,
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Cookie设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'cookie' => [
|
||||
// cookie 名称前缀
|
||||
'prefix' => '',
|
||||
// cookie 保存时间
|
||||
'expire' => 0,
|
||||
// cookie 保存路径
|
||||
'path' => '/',
|
||||
// cookie 有效域名
|
||||
'domain' => '',
|
||||
// cookie 启用安全传输
|
||||
'secure' => false,
|
||||
// httponly设置
|
||||
'httponly' => '',
|
||||
// 是否使用 setcookie
|
||||
'setcookie' => true,
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 数据库设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'database' => [
|
||||
// 数据库类型
|
||||
'type' => 'mysql',
|
||||
// 数据库连接DSN配置
|
||||
'dsn' => '',
|
||||
// 服务器地址
|
||||
'hostname' => '127.0.0.1',
|
||||
// 数据库名
|
||||
'database' => '',
|
||||
// 数据库用户名
|
||||
'username' => 'root',
|
||||
// 数据库密码
|
||||
'password' => '',
|
||||
// 数据库连接端口
|
||||
'hostport' => '',
|
||||
// 数据库连接参数
|
||||
'params' => [],
|
||||
// 数据库编码默认采用utf8
|
||||
'charset' => 'utf8',
|
||||
// 数据库表前缀
|
||||
'prefix' => '',
|
||||
// 数据库调试模式
|
||||
'debug' => false,
|
||||
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
|
||||
'deploy' => 0,
|
||||
// 数据库读写是否分离 主从式有效
|
||||
'rw_separate' => false,
|
||||
// 读写分离后 主服务器数量
|
||||
'master_num' => 1,
|
||||
// 指定从服务器序号
|
||||
'slave_no' => '',
|
||||
// 是否严格检查字段是否存在
|
||||
'fields_strict' => true,
|
||||
// 数据集返回类型
|
||||
'resultset_type' => 'array',
|
||||
// 自动写入时间戳字段
|
||||
'auto_timestamp' => false,
|
||||
// 时间字段取出后的默认时间格式
|
||||
'datetime_format' => 'Y-m-d H:i:s',
|
||||
// 是否需要进行SQL性能分析
|
||||
'sql_explain' => false,
|
||||
// 查询对象
|
||||
'query' => '\\think\\db\\Query',
|
||||
],
|
||||
|
||||
//分页配置
|
||||
'paginate' => [
|
||||
'type' => 'bootstrap',
|
||||
'var_page' => 'page',
|
||||
'list_rows' => 15,
|
||||
],
|
||||
|
||||
//控制台配置
|
||||
'console' => [
|
||||
'name' => 'Think Console',
|
||||
'version' => '0.1',
|
||||
'user' => null,
|
||||
'auto_path' => '',
|
||||
],
|
||||
|
||||
// 中间件配置
|
||||
'middleware' => [
|
||||
'default_namespace' => 'app\\http\\middleware\\',
|
||||
],
|
||||
];
|
||||
726
Server/thinkphp/helper.php
Normal file
726
Server/thinkphp/helper.php
Normal file
@@ -0,0 +1,726 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
//------------------------
|
||||
// ThinkPHP 助手函数
|
||||
//-------------------------
|
||||
|
||||
use think\Container;
|
||||
use think\Db;
|
||||
use think\exception\HttpException;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cookie;
|
||||
use think\facade\Debug;
|
||||
use think\facade\Env;
|
||||
use think\facade\Hook;
|
||||
use think\facade\Lang;
|
||||
use think\facade\Log;
|
||||
use think\facade\Request;
|
||||
use think\facade\Route;
|
||||
use think\facade\Session;
|
||||
use think\facade\Url;
|
||||
use think\Response;
|
||||
use think\route\RuleItem;
|
||||
|
||||
if (!function_exists('abort')) {
|
||||
/**
|
||||
* 抛出HTTP异常
|
||||
* @param integer|Response $code 状态码 或者 Response对象实例
|
||||
* @param string $message 错误信息
|
||||
* @param array $header 参数
|
||||
*/
|
||||
function abort($code, $message = null, $header = [])
|
||||
{
|
||||
if ($code instanceof Response) {
|
||||
throw new HttpResponseException($code);
|
||||
} else {
|
||||
throw new HttpException($code, $message, null, $header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('action')) {
|
||||
/**
|
||||
* 调用模块的操作方法 参数格式 [模块/控制器/]操作
|
||||
* @param string $url 调用地址
|
||||
* @param string|array $vars 调用参数 支持字符串和数组
|
||||
* @param string $layer 要调用的控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return mixed
|
||||
*/
|
||||
function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
|
||||
{
|
||||
return app()->action($url, $vars, $layer, $appendSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('app')) {
|
||||
/**
|
||||
* 快速获取容器中的实例 支持依赖注入
|
||||
* @param string $name 类名或标识 默认获取当前应用实例
|
||||
* @param array $args 参数
|
||||
* @param bool $newInstance 是否每次创建新的实例
|
||||
* @return mixed|\think\App
|
||||
*/
|
||||
function app($name = 'think\App', $args = [], $newInstance = false)
|
||||
{
|
||||
return Container::get($name, $args, $newInstance);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('behavior')) {
|
||||
/**
|
||||
* 执行某个行为(run方法) 支持依赖注入
|
||||
* @param mixed $behavior 行为类名或者别名
|
||||
* @param mixed $args 参数
|
||||
* @return mixed
|
||||
*/
|
||||
function behavior($behavior, $args = null)
|
||||
{
|
||||
return Hook::exec($behavior, $args);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('bind')) {
|
||||
/**
|
||||
* 绑定一个类到容器
|
||||
* @access public
|
||||
* @param string $abstract 类标识、接口
|
||||
* @param mixed $concrete 要绑定的类、闭包或者实例
|
||||
* @return Container
|
||||
*/
|
||||
function bind($abstract, $concrete = null)
|
||||
{
|
||||
return Container::getInstance()->bindTo($abstract, $concrete);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('cache')) {
|
||||
/**
|
||||
* 缓存管理
|
||||
* @param mixed $name 缓存名称,如果为数组表示进行缓存设置
|
||||
* @param mixed $value 缓存值
|
||||
* @param mixed $options 缓存参数
|
||||
* @param string $tag 缓存标签
|
||||
* @return mixed
|
||||
*/
|
||||
function cache($name, $value = '', $options = null, $tag = null)
|
||||
{
|
||||
if (is_array($options)) {
|
||||
// 缓存操作的同时初始化
|
||||
Cache::connect($options);
|
||||
} elseif (is_array($name)) {
|
||||
// 缓存初始化
|
||||
return Cache::connect($name);
|
||||
}
|
||||
|
||||
if ('' === $value) {
|
||||
// 获取缓存
|
||||
return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
|
||||
} elseif (is_null($value)) {
|
||||
// 删除缓存
|
||||
return Cache::rm($name);
|
||||
}
|
||||
|
||||
// 缓存数据
|
||||
if (is_array($options)) {
|
||||
$expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
|
||||
} else {
|
||||
$expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
|
||||
}
|
||||
|
||||
if (is_null($tag)) {
|
||||
return Cache::set($name, $value, $expire);
|
||||
} else {
|
||||
return Cache::tag($tag)->set($name, $value, $expire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('call')) {
|
||||
/**
|
||||
* 调用反射执行callable 支持依赖注入
|
||||
* @param mixed $callable 支持闭包等callable写法
|
||||
* @param array $args 参数
|
||||
* @return mixed
|
||||
*/
|
||||
function call($callable, $args = [])
|
||||
{
|
||||
return Container::getInstance()->invoke($callable, $args);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('class_basename')) {
|
||||
/**
|
||||
* 获取类名(不包含命名空间)
|
||||
*
|
||||
* @param string|object $class
|
||||
* @return string
|
||||
*/
|
||||
function class_basename($class)
|
||||
{
|
||||
$class = is_object($class) ? get_class($class) : $class;
|
||||
return basename(str_replace('\\', '/', $class));
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('class_uses_recursive')) {
|
||||
/**
|
||||
*获取一个类里所有用到的trait,包括父类的
|
||||
*
|
||||
* @param $class
|
||||
* @return array
|
||||
*/
|
||||
function class_uses_recursive($class)
|
||||
{
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$classes = array_merge([$class => $class], class_parents($class));
|
||||
foreach ($classes as $class) {
|
||||
$results += trait_uses_recursive($class);
|
||||
}
|
||||
|
||||
return array_unique($results);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('config')) {
|
||||
/**
|
||||
* 获取和设置配置参数
|
||||
* @param string|array $name 参数名
|
||||
* @param mixed $value 参数值
|
||||
* @return mixed
|
||||
*/
|
||||
function config($name = '', $value = null)
|
||||
{
|
||||
if (is_null($value) && is_string($name)) {
|
||||
if ('.' == substr($name, -1)) {
|
||||
return Config::pull(substr($name, 0, -1));
|
||||
}
|
||||
|
||||
return 0 === strpos($name, '?') ? Config::has(substr($name, 1)) : Config::get($name);
|
||||
} else {
|
||||
return Config::set($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('container')) {
|
||||
/**
|
||||
* 获取容器对象实例
|
||||
* @return Container
|
||||
*/
|
||||
function container()
|
||||
{
|
||||
return Container::getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('controller')) {
|
||||
/**
|
||||
* 实例化控制器 格式:[模块/]控制器
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return \think\Controller
|
||||
*/
|
||||
function controller($name, $layer = 'controller', $appendSuffix = false)
|
||||
{
|
||||
return app()->controller($name, $layer, $appendSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('cookie')) {
|
||||
/**
|
||||
* Cookie管理
|
||||
* @param string|array $name cookie名称,如果为数组表示进行cookie设置
|
||||
* @param mixed $value cookie值
|
||||
* @param mixed $option 参数
|
||||
* @return mixed
|
||||
*/
|
||||
function cookie($name, $value = '', $option = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
// 初始化
|
||||
Cookie::init($name);
|
||||
} elseif (is_null($name)) {
|
||||
// 清除
|
||||
Cookie::clear($value);
|
||||
} elseif ('' === $value) {
|
||||
// 获取
|
||||
return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name);
|
||||
} elseif (is_null($value)) {
|
||||
// 删除
|
||||
return Cookie::delete($name);
|
||||
} else {
|
||||
// 设置
|
||||
return Cookie::set($name, $value, $option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('db')) {
|
||||
/**
|
||||
* 实例化数据库类
|
||||
* @param string $name 操作的数据表名称(不含前缀)
|
||||
* @param array|string $config 数据库配置参数
|
||||
* @param bool $force 是否强制重新连接
|
||||
* @return \think\db\Query
|
||||
*/
|
||||
function db($name = '', $config = [], $force = true)
|
||||
{
|
||||
return Db::connect($config, $force)->name($name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('debug')) {
|
||||
/**
|
||||
* 记录时间(微秒)和内存使用情况
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位 如果是m 表示统计内存占用
|
||||
* @return mixed
|
||||
*/
|
||||
function debug($start, $end = '', $dec = 6)
|
||||
{
|
||||
if ('' == $end) {
|
||||
Debug::remark($start);
|
||||
} else {
|
||||
return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('download')) {
|
||||
/**
|
||||
* 获取\think\response\Download对象实例
|
||||
* @param string $filename 要下载的文件
|
||||
* @param string $name 显示文件名
|
||||
* @param bool $content 是否为内容
|
||||
* @param integer $expire 有效期(秒)
|
||||
* @return \think\response\Download
|
||||
*/
|
||||
function download($filename, $name = '', $content = false, $expire = 360, $openinBrowser = false)
|
||||
{
|
||||
return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire)->openinBrowser($openinBrowser);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('dump')) {
|
||||
/**
|
||||
* 浏览器友好的变量输出
|
||||
* @param mixed $var 变量
|
||||
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
|
||||
* @param string $label 标签 默认为空
|
||||
* @return void|string
|
||||
*/
|
||||
function dump($var, $echo = true, $label = null)
|
||||
{
|
||||
return Debug::dump($var, $echo, $label);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('env')) {
|
||||
/**
|
||||
* 获取环境变量值
|
||||
* @access public
|
||||
* @param string $name 环境变量名(支持二级 .号分割)
|
||||
* @param string $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
function env($name = null, $default = null)
|
||||
{
|
||||
return Env::get($name, $default);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('exception')) {
|
||||
/**
|
||||
* 抛出异常处理
|
||||
*
|
||||
* @param string $msg 异常消息
|
||||
* @param integer $code 异常代码 默认为0
|
||||
* @param string $exception 异常类
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
function exception($msg, $code = 0, $exception = '')
|
||||
{
|
||||
$e = $exception ?: '\think\Exception';
|
||||
throw new $e($msg, $code);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('halt')) {
|
||||
/**
|
||||
* 调试变量并且中断输出
|
||||
* @param mixed $var 调试变量或者信息
|
||||
*/
|
||||
function halt($var)
|
||||
{
|
||||
dump($var);
|
||||
|
||||
throw new HttpResponseException(new Response);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('input')) {
|
||||
/**
|
||||
* 获取输入数据 支持默认值和过滤
|
||||
* @param string $key 获取的变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $filter 过滤方法
|
||||
* @return mixed
|
||||
*/
|
||||
function input($key = '', $default = null, $filter = '')
|
||||
{
|
||||
if (0 === strpos($key, '?')) {
|
||||
$key = substr($key, 1);
|
||||
$has = true;
|
||||
}
|
||||
|
||||
if ($pos = strpos($key, '.')) {
|
||||
// 指定参数来源
|
||||
$method = substr($key, 0, $pos);
|
||||
if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
|
||||
$key = substr($key, $pos + 1);
|
||||
} else {
|
||||
$method = 'param';
|
||||
}
|
||||
} else {
|
||||
// 默认为自动判断
|
||||
$method = 'param';
|
||||
}
|
||||
|
||||
if (isset($has)) {
|
||||
return request()->has($key, $method, $default);
|
||||
} else {
|
||||
return request()->$method($key, $default, $filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('json')) {
|
||||
/**
|
||||
* 获取\think\response\Json对象实例
|
||||
* @param mixed $data 返回的数据
|
||||
* @param integer $code 状态码
|
||||
* @param array $header 头部
|
||||
* @param array $options 参数
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
function json($data = [], $code = 200, $header = [], $options = [])
|
||||
{
|
||||
return Response::create($data, 'json', $code, $header, $options);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('jsonp')) {
|
||||
/**
|
||||
* 获取\think\response\Jsonp对象实例
|
||||
* @param mixed $data 返回的数据
|
||||
* @param integer $code 状态码
|
||||
* @param array $header 头部
|
||||
* @param array $options 参数
|
||||
* @return \think\response\Jsonp
|
||||
*/
|
||||
function jsonp($data = [], $code = 200, $header = [], $options = [])
|
||||
{
|
||||
return Response::create($data, 'jsonp', $code, $header, $options);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('lang')) {
|
||||
/**
|
||||
* 获取语言变量值
|
||||
* @param string $name 语言变量名
|
||||
* @param array $vars 动态变量值
|
||||
* @param string $lang 语言
|
||||
* @return mixed
|
||||
*/
|
||||
function lang($name, $vars = [], $lang = '')
|
||||
{
|
||||
return Lang::get($name, $vars, $lang);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('model')) {
|
||||
/**
|
||||
* 实例化Model
|
||||
* @param string $name Model名称
|
||||
* @param string $layer 业务层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return \think\Model
|
||||
*/
|
||||
function model($name = '', $layer = 'model', $appendSuffix = false)
|
||||
{
|
||||
return app()->model($name, $layer, $appendSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('parse_name')) {
|
||||
/**
|
||||
* 字符串命名风格转换
|
||||
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
|
||||
* @param string $name 字符串
|
||||
* @param integer $type 转换类型
|
||||
* @param bool $ucfirst 首字母是否大写(驼峰规则)
|
||||
* @return string
|
||||
*/
|
||||
function parse_name($name, $type = 0, $ucfirst = true)
|
||||
{
|
||||
if ($type) {
|
||||
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
|
||||
return strtoupper($match[1]);
|
||||
}, $name);
|
||||
|
||||
return $ucfirst ? ucfirst($name) : lcfirst($name);
|
||||
} else {
|
||||
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('redirect')) {
|
||||
/**
|
||||
* 获取\think\response\Redirect对象实例
|
||||
* @param mixed $url 重定向地址 支持Url::build方法的地址
|
||||
* @param array|integer $params 额外参数
|
||||
* @param integer $code 状态码
|
||||
* @return \think\response\Redirect
|
||||
*/
|
||||
function redirect($url = [], $params = [], $code = 302)
|
||||
{
|
||||
if (is_integer($params)) {
|
||||
$code = $params;
|
||||
$params = [];
|
||||
}
|
||||
|
||||
return Response::create($url, 'redirect', $code)->params($params);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('request')) {
|
||||
/**
|
||||
* 获取当前Request对象实例
|
||||
* @return Request
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
return app('request');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('response')) {
|
||||
/**
|
||||
* 创建普通 Response 对象实例
|
||||
* @param mixed $data 输出数据
|
||||
* @param int|string $code 状态码
|
||||
* @param array $header 头信息
|
||||
* @param string $type
|
||||
* @return Response
|
||||
*/
|
||||
function response($data = '', $code = 200, $header = [], $type = 'html')
|
||||
{
|
||||
return Response::create($data, $type, $code, $header);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('route')) {
|
||||
/**
|
||||
* 路由注册
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
function route($rule, $route, $option = [], $pattern = [])
|
||||
{
|
||||
return Route::rule($rule, $route, '*', $option, $pattern);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('session')) {
|
||||
/**
|
||||
* Session管理
|
||||
* @param string|array $name session名称,如果为数组表示进行session设置
|
||||
* @param mixed $value session值
|
||||
* @param string $prefix 前缀
|
||||
* @return mixed
|
||||
*/
|
||||
function session($name, $value = '', $prefix = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
// 初始化
|
||||
Session::init($name);
|
||||
} elseif (is_null($name)) {
|
||||
// 清除
|
||||
Session::clear($value);
|
||||
} elseif ('' === $value) {
|
||||
// 判断或获取
|
||||
return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix);
|
||||
} elseif (is_null($value)) {
|
||||
// 删除
|
||||
return Session::delete($name, $prefix);
|
||||
} else {
|
||||
// 设置
|
||||
return Session::set($name, $value, $prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('token')) {
|
||||
/**
|
||||
* 生成表单令牌
|
||||
* @param string $name 令牌名称
|
||||
* @param mixed $type 令牌生成方法
|
||||
* @return string
|
||||
*/
|
||||
function token($name = '__token__', $type = 'md5')
|
||||
{
|
||||
$token = Request::token($name, $type);
|
||||
|
||||
return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('trace')) {
|
||||
/**
|
||||
* 记录日志信息
|
||||
* @param mixed $log log信息 支持字符串和数组
|
||||
* @param string $level 日志级别
|
||||
* @return array|void
|
||||
*/
|
||||
function trace($log = '[think]', $level = 'log')
|
||||
{
|
||||
if ('[think]' === $log) {
|
||||
return Log::getLog();
|
||||
} else {
|
||||
Log::record($log, $level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('trait_uses_recursive')) {
|
||||
/**
|
||||
* 获取一个trait里所有引用到的trait
|
||||
*
|
||||
* @param string $trait
|
||||
* @return array
|
||||
*/
|
||||
function trait_uses_recursive($trait)
|
||||
{
|
||||
$traits = class_uses($trait);
|
||||
foreach ($traits as $trait) {
|
||||
$traits += trait_uses_recursive($trait);
|
||||
}
|
||||
|
||||
return $traits;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('url')) {
|
||||
/**
|
||||
* Url生成
|
||||
* @param string $url 路由地址
|
||||
* @param string|array $vars 变量
|
||||
* @param bool|string $suffix 生成的URL后缀
|
||||
* @param bool|string $domain 域名
|
||||
* @return string
|
||||
*/
|
||||
function url($url = '', $vars = '', $suffix = true, $domain = false)
|
||||
{
|
||||
return Url::build($url, $vars, $suffix, $domain);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('validate')) {
|
||||
/**
|
||||
* 实例化验证器
|
||||
* @param string $name 验证器名称
|
||||
* @param string $layer 业务层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return \think\Validate
|
||||
*/
|
||||
function validate($name = '', $layer = 'validate', $appendSuffix = false)
|
||||
{
|
||||
return app()->validate($name, $layer, $appendSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('view')) {
|
||||
/**
|
||||
* 渲染模板输出
|
||||
* @param string $template 模板文件
|
||||
* @param array $vars 模板变量
|
||||
* @param integer $code 状态码
|
||||
* @param callable $filter 内容过滤
|
||||
* @return \think\response\View
|
||||
*/
|
||||
function view($template = '', $vars = [], $code = 200, $filter = null)
|
||||
{
|
||||
return Response::create($template, 'view', $code)->assign($vars)->filter($filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('widget')) {
|
||||
/**
|
||||
* 渲染输出Widget
|
||||
* @param string $name Widget名称
|
||||
* @param array $data 传入的参数
|
||||
* @return mixed
|
||||
*/
|
||||
function widget($name, $data = [])
|
||||
{
|
||||
$result = app()->action($name, $data, 'widget');
|
||||
|
||||
if (is_object($result)) {
|
||||
$result = $result->getContent();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('xml')) {
|
||||
/**
|
||||
* 获取\think\response\Xml对象实例
|
||||
* @param mixed $data 返回的数据
|
||||
* @param integer $code 状态码
|
||||
* @param array $header 头部
|
||||
* @param array $options 参数
|
||||
* @return \think\response\Xml
|
||||
*/
|
||||
function xml($data = [], $code = 200, $header = [], $options = [])
|
||||
{
|
||||
return Response::create($data, 'xml', $code, $header, $options);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('yaconf')) {
|
||||
/**
|
||||
* 获取yaconf配置
|
||||
*
|
||||
* @param string $name 配置参数名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
function yaconf($name, $default = null)
|
||||
{
|
||||
return Config::yaconf($name, $default);
|
||||
}
|
||||
}
|
||||
144
Server/thinkphp/lang/zh-cn.php
Normal file
144
Server/thinkphp/lang/zh-cn.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 核心中文语言包
|
||||
return [
|
||||
// 系统错误提示
|
||||
'Undefined variable' => '未定义变量',
|
||||
'Undefined index' => '未定义数组索引',
|
||||
'Undefined offset' => '未定义数组下标',
|
||||
'Parse error' => '语法解析错误',
|
||||
'Type error' => '类型错误',
|
||||
'Fatal error' => '致命错误',
|
||||
'syntax error' => '语法错误',
|
||||
|
||||
// 框架核心错误提示
|
||||
'dispatch type not support' => '不支持的调度类型',
|
||||
'method param miss' => '方法参数错误',
|
||||
'method not exists' => '方法不存在',
|
||||
'function not exists' => '函数不存在',
|
||||
'file not exists' => '文件不存在',
|
||||
'module not exists' => '模块不存在',
|
||||
'controller not exists' => '控制器不存在',
|
||||
'class not exists' => '类不存在',
|
||||
'property not exists' => '类的属性不存在',
|
||||
'template not exists' => '模板文件不存在',
|
||||
'illegal controller name' => '非法的控制器名称',
|
||||
'illegal action name' => '非法的操作名称',
|
||||
'url suffix deny' => '禁止的URL后缀访问',
|
||||
'Route Not Found' => '当前访问路由未定义或不匹配',
|
||||
'Undefined db type' => '未定义数据库类型',
|
||||
'variable type error' => '变量类型错误',
|
||||
'PSR-4 error' => 'PSR-4 规范错误',
|
||||
'not support total' => '简洁模式下不能获取数据总数',
|
||||
'not support last' => '简洁模式下不能获取最后一页',
|
||||
'error session handler' => '错误的SESSION处理器类',
|
||||
'not allow php tag' => '模板不允许使用PHP语法',
|
||||
'not support' => '不支持',
|
||||
'redisd master' => 'Redisd 主服务器错误',
|
||||
'redisd slave' => 'Redisd 从服务器错误',
|
||||
'must run at sae' => '必须在SAE运行',
|
||||
'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务',
|
||||
'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
|
||||
'fields not exists' => '数据表字段不存在',
|
||||
'where express error' => '查询表达式错误',
|
||||
'order express error' => '排序表达式错误',
|
||||
'no data to update' => '没有任何数据需要更新',
|
||||
'miss data to insert' => '缺少需要写入的数据',
|
||||
'not support data' => '不支持的数据表达式',
|
||||
'miss complex primary data' => '缺少复合主键数据',
|
||||
'miss update condition' => '缺少更新条件',
|
||||
'model data Not Found' => '模型数据不存在',
|
||||
'table data not Found' => '表数据不存在',
|
||||
'delete without condition' => '没有条件不会执行删除操作',
|
||||
'miss relation data' => '缺少关联表数据',
|
||||
'tag attr must' => '模板标签属性必须',
|
||||
'tag error' => '模板标签错误',
|
||||
'cache write error' => '缓存写入失败',
|
||||
'sae mc write error' => 'SAE mc 写入错误',
|
||||
'route name not exists' => '路由标识不存在(或参数不够)',
|
||||
'invalid request' => '非法请求',
|
||||
'bind attr has exists' => '模型的属性已经存在',
|
||||
'relation data not exists' => '关联数据不存在',
|
||||
'relation not support' => '关联不支持',
|
||||
'chunk not support order' => 'Chunk不支持调用order方法',
|
||||
'route pattern error' => '路由变量规则定义错误',
|
||||
'route behavior will not support' => '路由行为废弃(使用中间件替代)',
|
||||
'closure not support cache(true)' => '使用闭包查询不支持cache(true),请指定缓存Key',
|
||||
|
||||
// 上传错误信息
|
||||
'unknown upload error' => '未知上传错误!',
|
||||
'file write error' => '文件写入失败!',
|
||||
'upload temp dir not found' => '找不到临时文件夹!',
|
||||
'no file to uploaded' => '没有文件被上传!',
|
||||
'only the portion of file is uploaded' => '文件只有部分被上传!',
|
||||
'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!',
|
||||
'upload write error' => '文件上传保存错误!',
|
||||
'has the same filename: {:filename}' => '存在同名文件:{:filename}',
|
||||
'upload illegal files' => '非法上传文件',
|
||||
'illegal image files' => '非法图片文件',
|
||||
'extensions to upload is not allowed' => '上传文件后缀不允许',
|
||||
'mimetype to upload is not allowed' => '上传文件MIME类型不允许!',
|
||||
'filesize not match' => '上传文件大小不符!',
|
||||
'directory {:path} creation failed' => '目录 {:path} 创建失败!',
|
||||
|
||||
'The middleware must return Response instance' => '中间件方法必须返回Response对象实例',
|
||||
'The queue was exhausted, with no response returned' => '中间件队列为空',
|
||||
// Validate Error Message
|
||||
':attribute require' => ':attribute不能为空',
|
||||
':attribute must' => ':attribute必须',
|
||||
':attribute must be numeric' => ':attribute必须是数字',
|
||||
':attribute must be integer' => ':attribute必须是整数',
|
||||
':attribute must be float' => ':attribute必须是浮点数',
|
||||
':attribute must be bool' => ':attribute必须是布尔值',
|
||||
':attribute not a valid email address' => ':attribute格式不符',
|
||||
':attribute not a valid mobile' => ':attribute格式不符',
|
||||
':attribute must be a array' => ':attribute必须是数组',
|
||||
':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1',
|
||||
':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式',
|
||||
':attribute not a valid file' => ':attribute不是有效的上传文件',
|
||||
':attribute not a valid image' => ':attribute不是有效的图像文件',
|
||||
':attribute must be alpha' => ':attribute只能是字母',
|
||||
':attribute must be alpha-numeric' => ':attribute只能是字母和数字',
|
||||
':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-',
|
||||
':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP',
|
||||
':attribute must be chinese' => ':attribute只能是汉字',
|
||||
':attribute must be chinese or alpha' => ':attribute只能是汉字、字母',
|
||||
':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字',
|
||||
':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
|
||||
':attribute not a valid url' => ':attribute不是有效的URL地址',
|
||||
':attribute not a valid ip' => ':attribute不是有效的IP地址',
|
||||
':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule',
|
||||
':attribute must be in :rule' => ':attribute必须在 :rule 范围内',
|
||||
':attribute be notin :rule' => ':attribute不能在 :rule 范围内',
|
||||
':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间',
|
||||
':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间',
|
||||
'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule',
|
||||
'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule',
|
||||
'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule',
|
||||
':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule',
|
||||
':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule',
|
||||
':attribute not within :rule' => '不在有效期内 :rule',
|
||||
'access IP is not allowed' => '不允许的IP访问',
|
||||
'access IP denied' => '禁止的IP访问',
|
||||
':attribute out of accord with :2' => ':attribute和确认字段:2不一致',
|
||||
':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同',
|
||||
':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule',
|
||||
':attribute must greater than :rule' => ':attribute必须大于 :rule',
|
||||
':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule',
|
||||
':attribute must less than :rule' => ':attribute必须小于 :rule',
|
||||
':attribute must equal :rule' => ':attribute必须等于 :rule',
|
||||
':attribute has exists' => ':attribute已存在',
|
||||
':attribute not conform to the rules' => ':attribute不符合指定规则',
|
||||
'invalid Request method' => '无效的请求类型',
|
||||
'invalid token' => '令牌数据无效',
|
||||
'not conform to the rules' => '规则错误',
|
||||
];
|
||||
979
Server/thinkphp/library/think/App.php
Normal file
979
Server/thinkphp/library/think/App.php
Normal file
@@ -0,0 +1,979 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\exception\ClassNotFoundException;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\route\Dispatch;
|
||||
|
||||
/**
|
||||
* App 应用管理
|
||||
*/
|
||||
class App extends Container
|
||||
{
|
||||
const VERSION = '5.1.41 LTS';
|
||||
|
||||
/**
|
||||
* 当前模块路径
|
||||
* @var string
|
||||
*/
|
||||
protected $modulePath;
|
||||
|
||||
/**
|
||||
* 应用调试模式
|
||||
* @var bool
|
||||
*/
|
||||
protected $appDebug = true;
|
||||
|
||||
/**
|
||||
* 应用开始时间
|
||||
* @var float
|
||||
*/
|
||||
protected $beginTime;
|
||||
|
||||
/**
|
||||
* 应用内存初始占用
|
||||
* @var integer
|
||||
*/
|
||||
protected $beginMem;
|
||||
|
||||
/**
|
||||
* 应用类库命名空间
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'app';
|
||||
|
||||
/**
|
||||
* 应用类库后缀
|
||||
* @var bool
|
||||
*/
|
||||
protected $suffix = false;
|
||||
|
||||
/**
|
||||
* 严格路由检测
|
||||
* @var bool
|
||||
*/
|
||||
protected $routeMust;
|
||||
|
||||
/**
|
||||
* 应用类库目录
|
||||
* @var string
|
||||
*/
|
||||
protected $appPath;
|
||||
|
||||
/**
|
||||
* 框架目录
|
||||
* @var string
|
||||
*/
|
||||
protected $thinkPath;
|
||||
|
||||
/**
|
||||
* 应用根目录
|
||||
* @var string
|
||||
*/
|
||||
protected $rootPath;
|
||||
|
||||
/**
|
||||
* 运行时目录
|
||||
* @var string
|
||||
*/
|
||||
protected $runtimePath;
|
||||
|
||||
/**
|
||||
* 配置目录
|
||||
* @var string
|
||||
*/
|
||||
protected $configPath;
|
||||
|
||||
/**
|
||||
* 路由目录
|
||||
* @var string
|
||||
*/
|
||||
protected $routePath;
|
||||
|
||||
/**
|
||||
* 配置后缀
|
||||
* @var string
|
||||
*/
|
||||
protected $configExt;
|
||||
|
||||
/**
|
||||
* 应用调度实例
|
||||
* @var Dispatch
|
||||
*/
|
||||
protected $dispatch;
|
||||
|
||||
/**
|
||||
* 绑定模块(控制器)
|
||||
* @var string
|
||||
*/
|
||||
protected $bindModule;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @var bool
|
||||
*/
|
||||
protected $initialized = false;
|
||||
|
||||
public function __construct($appPath = '')
|
||||
{
|
||||
$this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
|
||||
$this->path($appPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定模块或者控制器
|
||||
* @access public
|
||||
* @param string $bind
|
||||
* @return $this
|
||||
*/
|
||||
public function bind($bind)
|
||||
{
|
||||
$this->bindModule = $bind;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用类库目录
|
||||
* @access public
|
||||
* @param string $path 路径
|
||||
* @return $this
|
||||
*/
|
||||
public function path($path)
|
||||
{
|
||||
$this->appPath = $path ? realpath($path) . DIRECTORY_SEPARATOR : $this->getAppPath();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->initialized = true;
|
||||
$this->beginTime = microtime(true);
|
||||
$this->beginMem = memory_get_usage();
|
||||
|
||||
$this->rootPath = dirname($this->appPath) . DIRECTORY_SEPARATOR;
|
||||
$this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
|
||||
$this->routePath = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
|
||||
$this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
|
||||
|
||||
static::setInstance($this);
|
||||
|
||||
$this->instance('app', $this);
|
||||
|
||||
// 加载环境变量配置文件
|
||||
if (is_file($this->rootPath . '.env')) {
|
||||
$this->env->load($this->rootPath . '.env');
|
||||
}
|
||||
|
||||
$this->configExt = $this->env->get('config_ext', '.php');
|
||||
|
||||
// 加载惯例配置文件
|
||||
$this->config->set(include $this->thinkPath . 'convention.php');
|
||||
|
||||
// 设置路径环境变量
|
||||
$this->env->set([
|
||||
'think_path' => $this->thinkPath,
|
||||
'root_path' => $this->rootPath,
|
||||
'app_path' => $this->appPath,
|
||||
'config_path' => $this->configPath,
|
||||
'route_path' => $this->routePath,
|
||||
'runtime_path' => $this->runtimePath,
|
||||
'extend_path' => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR,
|
||||
'vendor_path' => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
|
||||
]);
|
||||
|
||||
$this->namespace = $this->env->get('app_namespace', $this->namespace);
|
||||
$this->env->set('app_namespace', $this->namespace);
|
||||
|
||||
// 注册应用命名空间
|
||||
Loader::addNamespace($this->namespace, $this->appPath);
|
||||
|
||||
// 初始化应用
|
||||
$this->init();
|
||||
|
||||
// 开启类名后缀
|
||||
$this->suffix = $this->config('app.class_suffix');
|
||||
|
||||
// 应用调试模式
|
||||
$this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug'));
|
||||
$this->env->set('app_debug', $this->appDebug);
|
||||
|
||||
if (!$this->appDebug) {
|
||||
ini_set('display_errors', 'Off');
|
||||
} elseif (PHP_SAPI != 'cli') {
|
||||
//重新申请一块比较大的buffer
|
||||
if (ob_get_level() > 0) {
|
||||
$output = ob_get_clean();
|
||||
}
|
||||
ob_start();
|
||||
if (!empty($output)) {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册异常处理类
|
||||
if ($this->config('app.exception_handle')) {
|
||||
Error::setExceptionHandler($this->config('app.exception_handle'));
|
||||
}
|
||||
|
||||
// 注册根命名空间
|
||||
if (!empty($this->config('app.root_namespace'))) {
|
||||
Loader::addNamespace($this->config('app.root_namespace'));
|
||||
}
|
||||
|
||||
// 加载composer autofile文件
|
||||
Loader::loadComposerAutoloadFiles();
|
||||
|
||||
// 注册类库别名
|
||||
Loader::addClassAlias($this->config->pull('alias'));
|
||||
|
||||
// 数据库配置初始化
|
||||
Db::init($this->config->pull('database'));
|
||||
|
||||
// 设置系统时区
|
||||
date_default_timezone_set($this->config('app.default_timezone'));
|
||||
|
||||
// 读取语言包
|
||||
$this->loadLangPack();
|
||||
|
||||
// 路由初始化
|
||||
$this->routeInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用或模块
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @return void
|
||||
*/
|
||||
public function init($module = '')
|
||||
{
|
||||
// 定位模块目录
|
||||
$module = $module ? $module . DIRECTORY_SEPARATOR : '';
|
||||
$path = $this->appPath . $module;
|
||||
|
||||
// 加载初始化文件
|
||||
if (is_file($path . 'init.php')) {
|
||||
include $path . 'init.php';
|
||||
} elseif (is_file($this->runtimePath . $module . 'init.php')) {
|
||||
include $this->runtimePath . $module . 'init.php';
|
||||
} else {
|
||||
// 加载行为扩展文件
|
||||
if (is_file($path . 'tags.php')) {
|
||||
$tags = include $path . 'tags.php';
|
||||
if (is_array($tags)) {
|
||||
$this->hook->import($tags);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载公共文件
|
||||
if (is_file($path . 'common.php')) {
|
||||
include_once $path . 'common.php';
|
||||
}
|
||||
|
||||
if ('' == $module) {
|
||||
// 加载系统助手函数
|
||||
include $this->thinkPath . 'helper.php';
|
||||
}
|
||||
|
||||
// 加载中间件
|
||||
if (is_file($path . 'middleware.php')) {
|
||||
$middleware = include $path . 'middleware.php';
|
||||
if (is_array($middleware)) {
|
||||
$this->middleware->import($middleware);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册服务的容器对象实例
|
||||
if (is_file($path . 'provider.php')) {
|
||||
$provider = include $path . 'provider.php';
|
||||
if (is_array($provider)) {
|
||||
$this->bindTo($provider);
|
||||
}
|
||||
}
|
||||
|
||||
// 自动读取配置文件
|
||||
if (is_dir($path . 'config')) {
|
||||
$dir = $path . 'config' . DIRECTORY_SEPARATOR;
|
||||
} elseif (is_dir($this->configPath . $module)) {
|
||||
$dir = $this->configPath . $module;
|
||||
}
|
||||
|
||||
$files = isset($dir) ? scandir($dir) : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
|
||||
$this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->setModulePath($path);
|
||||
|
||||
if ($module) {
|
||||
// 对容器中的对象实例进行配置更新
|
||||
$this->containerConfigUpdate($module);
|
||||
}
|
||||
}
|
||||
|
||||
protected function containerConfigUpdate($module)
|
||||
{
|
||||
$config = $this->config->get();
|
||||
|
||||
// 注册异常处理类
|
||||
if ($config['app']['exception_handle']) {
|
||||
Error::setExceptionHandler($config['app']['exception_handle']);
|
||||
}
|
||||
|
||||
Db::init($config['database']);
|
||||
$this->middleware->setConfig($config['middleware']);
|
||||
$this->route->setConfig($config['app']);
|
||||
$this->request->init($config['app']);
|
||||
$this->cookie->init($config['cookie']);
|
||||
$this->view->init($config['template']);
|
||||
$this->log->init($config['log']);
|
||||
$this->session->setConfig($config['session']);
|
||||
$this->debug->setConfig($config['trace']);
|
||||
$this->cache->init($config['cache'], true);
|
||||
|
||||
// 加载当前模块语言包
|
||||
$this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php');
|
||||
|
||||
// 模块请求缓存检查
|
||||
$this->checkRequestCache(
|
||||
$config['app']['request_cache'],
|
||||
$config['app']['request_cache_expire'],
|
||||
$config['app']['request_cache_except']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行应用程序
|
||||
* @access public
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
try {
|
||||
// 初始化应用
|
||||
$this->initialize();
|
||||
|
||||
// 监听app_init
|
||||
$this->hook->listen('app_init');
|
||||
|
||||
if ($this->bindModule) {
|
||||
// 模块/控制器绑定
|
||||
$this->route->bind($this->bindModule);
|
||||
} elseif ($this->config('app.auto_bind_module')) {
|
||||
// 入口自动绑定
|
||||
$name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);
|
||||
if ($name && 'index' != $name && is_dir($this->appPath . $name)) {
|
||||
$this->route->bind($name);
|
||||
}
|
||||
}
|
||||
|
||||
// 监听app_dispatch
|
||||
$this->hook->listen('app_dispatch');
|
||||
|
||||
$dispatch = $this->dispatch;
|
||||
|
||||
if (empty($dispatch)) {
|
||||
// 路由检测
|
||||
$dispatch = $this->routeCheck()->init();
|
||||
}
|
||||
|
||||
// 记录当前调度信息
|
||||
$this->request->dispatch($dispatch);
|
||||
|
||||
// 记录路由和请求信息
|
||||
if ($this->appDebug) {
|
||||
$this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true));
|
||||
$this->log('[ HEADER ] ' . var_export($this->request->header(), true));
|
||||
$this->log('[ PARAM ] ' . var_export($this->request->param(), true));
|
||||
}
|
||||
|
||||
// 监听app_begin
|
||||
$this->hook->listen('app_begin');
|
||||
|
||||
// 请求缓存检查
|
||||
$this->checkRequestCache(
|
||||
$this->config('request_cache'),
|
||||
$this->config('request_cache_expire'),
|
||||
$this->config('request_cache_except')
|
||||
);
|
||||
|
||||
$data = null;
|
||||
} catch (HttpResponseException $exception) {
|
||||
$dispatch = null;
|
||||
$data = $exception->getResponse();
|
||||
}
|
||||
|
||||
$this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
|
||||
return is_null($data) ? $dispatch->run() : $data;
|
||||
});
|
||||
|
||||
$response = $this->middleware->dispatch($this->request);
|
||||
|
||||
// 监听app_end
|
||||
$this->hook->listen('app_end', $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function getRouteCacheKey()
|
||||
{
|
||||
if ($this->config->get('route_check_cache_key')) {
|
||||
$closure = $this->config->get('route_check_cache_key');
|
||||
$routeKey = $closure($this->request);
|
||||
} else {
|
||||
$routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method());
|
||||
}
|
||||
|
||||
return $routeKey;
|
||||
}
|
||||
|
||||
protected function loadLangPack()
|
||||
{
|
||||
// 读取默认语言
|
||||
$this->lang->range($this->config('app.default_lang'));
|
||||
|
||||
if ($this->config('app.lang_switch_on')) {
|
||||
// 开启多语言机制 检测当前语言
|
||||
$this->lang->detect();
|
||||
}
|
||||
|
||||
$this->request->setLangset($this->lang->range());
|
||||
|
||||
// 加载系统语言包
|
||||
$this->lang->load([
|
||||
$this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
|
||||
$this->appPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前地址的请求缓存
|
||||
* @access public
|
||||
* @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
|
||||
* @param mixed $expire 缓存有效期
|
||||
* @param array $except 缓存排除
|
||||
* @param string $tag 缓存标签
|
||||
* @return void
|
||||
*/
|
||||
public function checkRequestCache($key, $expire = null, $except = [], $tag = null)
|
||||
{
|
||||
$cache = $this->request->cache($key, $expire, $except, $tag);
|
||||
|
||||
if ($cache) {
|
||||
$this->setResponseCache($cache);
|
||||
}
|
||||
}
|
||||
|
||||
public function setResponseCache($cache)
|
||||
{
|
||||
list($key, $expire, $tag) = $cache;
|
||||
|
||||
if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {
|
||||
// 读取缓存
|
||||
$response = Response::create()->code(304);
|
||||
throw new HttpResponseException($response);
|
||||
} elseif ($this->cache->has($key)) {
|
||||
list($content, $header) = $this->cache->get($key);
|
||||
|
||||
$response = Response::create($content)->header($header);
|
||||
throw new HttpResponseException($response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前请求的调度信息
|
||||
* @access public
|
||||
* @param Dispatch $dispatch 调度信息
|
||||
* @return $this
|
||||
*/
|
||||
public function dispatch(Dispatch $dispatch)
|
||||
{
|
||||
$this->dispatch = $dispatch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录调试信息
|
||||
* @access public
|
||||
* @param mixed $msg 调试信息
|
||||
* @param string $type 信息类型
|
||||
* @return void
|
||||
*/
|
||||
public function log($msg, $type = 'info')
|
||||
{
|
||||
$this->appDebug && $this->log->record($msg, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置参数 为空则获取所有配置
|
||||
* @access public
|
||||
* @param string $name 配置参数名(支持二级配置 .号分割)
|
||||
* @return mixed
|
||||
*/
|
||||
public function config($name = '')
|
||||
{
|
||||
return $this->config->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由初始化 导入路由定义规则
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function routeInit()
|
||||
{
|
||||
// 路由检测
|
||||
if (is_dir($this->routePath)) {
|
||||
$files = glob($this->routePath . '*.php');
|
||||
foreach ($files as $file) {
|
||||
$rules = include $file;
|
||||
if (is_array($rules)) {
|
||||
$this->route->import($rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->route->config('route_annotation')) {
|
||||
// 自动生成路由定义
|
||||
if ($this->appDebug) {
|
||||
$suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');
|
||||
$this->build->buildRoute($suffix);
|
||||
}
|
||||
|
||||
$filename = $this->runtimePath . 'build_route.php';
|
||||
|
||||
if (is_file($filename)) {
|
||||
include $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* URL路由检测(根据PATH_INFO)
|
||||
* @access public
|
||||
* @return Dispatch
|
||||
*/
|
||||
public function routeCheck()
|
||||
{
|
||||
// 检测路由缓存
|
||||
if (!$this->appDebug && $this->config->get('route_check_cache')) {
|
||||
$routeKey = $this->getRouteCacheKey();
|
||||
$option = $this->config->get('route_cache_option');
|
||||
|
||||
if ($option && $this->cache->connect($option)->has($routeKey)) {
|
||||
return $this->cache->connect($option)->get($routeKey);
|
||||
} elseif ($this->cache->has($routeKey)) {
|
||||
return $this->cache->get($routeKey);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取应用调度信息
|
||||
$path = $this->request->path();
|
||||
|
||||
// 是否强制路由模式
|
||||
$must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must');
|
||||
|
||||
// 路由检测 返回一个Dispatch对象
|
||||
$dispatch = $this->route->check($path, $must);
|
||||
|
||||
if (!empty($routeKey)) {
|
||||
try {
|
||||
if ($option) {
|
||||
$this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
|
||||
} else {
|
||||
$this->cache->tag('route_cache')->set($routeKey, $dispatch);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 存在闭包的时候缓存无效
|
||||
}
|
||||
}
|
||||
|
||||
return $dispatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用的路由检测机制
|
||||
* @access public
|
||||
* @param bool $must 是否强制检测路由
|
||||
* @return $this
|
||||
*/
|
||||
public function routeMust($must = false)
|
||||
{
|
||||
$this->routeMust = $must;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模块和类名
|
||||
* @access protected
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 验证层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return array
|
||||
*/
|
||||
protected function parseModuleAndClass($name, $layer, $appendSuffix)
|
||||
{
|
||||
if (false !== strpos($name, '\\')) {
|
||||
$class = $name;
|
||||
$module = $this->request->module();
|
||||
} else {
|
||||
if (strpos($name, '/')) {
|
||||
list($module, $name) = explode('/', $name, 2);
|
||||
} else {
|
||||
$module = $this->request->module();
|
||||
}
|
||||
|
||||
$class = $this->parseClass($module, $layer, $name, $appendSuffix);
|
||||
}
|
||||
|
||||
return [$module, $class];
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化应用类库
|
||||
* @access public
|
||||
* @param string $name 类名称
|
||||
* @param string $layer 业务层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $common 公共模块名
|
||||
* @return object
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public function create($name, $layer, $appendSuffix = false, $common = 'common')
|
||||
{
|
||||
$guid = $name . $layer;
|
||||
|
||||
if ($this->__isset($guid)) {
|
||||
return $this->__get($guid);
|
||||
}
|
||||
|
||||
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
|
||||
|
||||
if (class_exists($class)) {
|
||||
$object = $this->__get($class);
|
||||
} else {
|
||||
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
|
||||
if (class_exists($class)) {
|
||||
$object = $this->__get($class);
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
$this->__set($guid, $class);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化(分层)模型
|
||||
* @access public
|
||||
* @param string $name Model名称
|
||||
* @param string $layer 业务层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $common 公共模块名
|
||||
* @return Model
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
|
||||
{
|
||||
return $this->create($name, $layer, $appendSuffix, $common);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化(分层)控制器 格式:[模块名/]控制器名
|
||||
* @access public
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $empty 空控制器名称
|
||||
* @return object
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
|
||||
{
|
||||
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
|
||||
|
||||
if (class_exists($class)) {
|
||||
return $this->make($class, true);
|
||||
} elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
|
||||
return $this->make($emptyClass, true);
|
||||
}
|
||||
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化验证类 格式:[模块名/]验证器名
|
||||
* @access public
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 验证层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $common 公共模块名
|
||||
* @return Validate
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
|
||||
{
|
||||
$name = $name ?: $this->config('default_validate');
|
||||
|
||||
if (empty($name)) {
|
||||
return new Validate;
|
||||
}
|
||||
|
||||
return $this->create($name, $layer, $appendSuffix, $common);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库初始化
|
||||
* @access public
|
||||
* @param mixed $config 数据库配置
|
||||
* @param bool|string $name 连接标识 true 强制重新连接
|
||||
* @return \think\db\Query
|
||||
*/
|
||||
public function db($config = [], $name = false)
|
||||
{
|
||||
return Db::connect($config, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
|
||||
* @access public
|
||||
* @param string $url 调用地址
|
||||
* @param string|array $vars 调用参数 支持字符串和数组
|
||||
* @param string $layer 要调用的控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return mixed
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
|
||||
{
|
||||
$info = pathinfo($url);
|
||||
$action = $info['basename'];
|
||||
$module = '.' != $info['dirname'] ? $info['dirname'] : $this->request->controller();
|
||||
$class = $this->controller($module, $layer, $appendSuffix);
|
||||
|
||||
if (is_scalar($vars)) {
|
||||
if (strpos($vars, '=')) {
|
||||
parse_str($vars, $vars);
|
||||
} else {
|
||||
$vars = [$vars];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->invokeMethod([$class, $action . $this->config('action_suffix')], $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析应用类的类名
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @param string $layer 层名 controller model ...
|
||||
* @param string $name 类名
|
||||
* @param bool $appendSuffix
|
||||
* @return string
|
||||
*/
|
||||
public function parseClass($module, $layer, $name, $appendSuffix = false)
|
||||
{
|
||||
$name = str_replace(['/', '.'], '\\', $name);
|
||||
$array = explode('\\', $name);
|
||||
$class = Loader::parseName(array_pop($array), 1) . ($this->suffix || $appendSuffix ? ucfirst($layer) : '');
|
||||
$path = $array ? implode('\\', $array) . '\\' : '';
|
||||
|
||||
return $this->namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取框架版本
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return static::VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为调试模式
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->appDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块路径
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getModulePath()
|
||||
{
|
||||
return $this->modulePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模块路径
|
||||
* @access public
|
||||
* @param string $path 路径
|
||||
* @return void
|
||||
*/
|
||||
public function setModulePath($path)
|
||||
{
|
||||
$this->modulePath = $path;
|
||||
$this->env->set('module_path', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用根目录
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getRootPath()
|
||||
{
|
||||
return $this->rootPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用类库目录
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAppPath()
|
||||
{
|
||||
if (is_null($this->appPath)) {
|
||||
$this->appPath = Loader::getRootPath() . 'application' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
return $this->appPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用运行时目录
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getRuntimePath()
|
||||
{
|
||||
return $this->runtimePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取核心框架目录
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getThinkPath()
|
||||
{
|
||||
return $this->thinkPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路由目录
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getRoutePath()
|
||||
{
|
||||
return $this->routePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用配置目录
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getConfigPath()
|
||||
{
|
||||
return $this->configPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置后缀
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getConfigExt()
|
||||
{
|
||||
return $this->configExt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用类库命名空间
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用类库命名空间
|
||||
* @access public
|
||||
* @param string $namespace 命名空间名称
|
||||
* @return $this
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用类库后缀
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function getSuffix()
|
||||
{
|
||||
return $this->suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用开启时间
|
||||
* @access public
|
||||
* @return float
|
||||
*/
|
||||
public function getBeginTime()
|
||||
{
|
||||
return $this->beginTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用初始内存占用
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getBeginMem()
|
||||
{
|
||||
return $this->beginMem;
|
||||
}
|
||||
|
||||
}
|
||||
415
Server/thinkphp/library/think/Build.php
Normal file
415
Server/thinkphp/library/think/Build.php
Normal file
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Build
|
||||
{
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 应用目录
|
||||
* @var string
|
||||
*/
|
||||
protected $basePath;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->basePath = $this->app->getAppPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的build资料创建目录和文件
|
||||
* @access public
|
||||
* @param array $build build列表
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
public function run(array $build = [], $namespace = 'app', $suffix = false)
|
||||
{
|
||||
// 锁定
|
||||
$lockfile = $this->basePath . 'build.lock';
|
||||
|
||||
if (is_writable($lockfile)) {
|
||||
return;
|
||||
} elseif (!touch($lockfile)) {
|
||||
throw new Exception('应用目录[' . $this->basePath . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~', 10006);
|
||||
}
|
||||
|
||||
foreach ($build as $module => $list) {
|
||||
if ('__dir__' == $module) {
|
||||
// 创建目录列表
|
||||
$this->buildDir($list);
|
||||
} elseif ('__file__' == $module) {
|
||||
// 创建文件列表
|
||||
$this->buildFile($list);
|
||||
} else {
|
||||
// 创建模块
|
||||
$this->module($module, $list, $namespace, $suffix);
|
||||
}
|
||||
}
|
||||
|
||||
// 解除锁定
|
||||
unlink($lockfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
* @access protected
|
||||
* @param array $list 目录列表
|
||||
* @return void
|
||||
*/
|
||||
protected function buildDir($list)
|
||||
{
|
||||
foreach ($list as $dir) {
|
||||
$this->checkDirBuild($this->basePath . $dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件
|
||||
* @access protected
|
||||
* @param array $list 文件列表
|
||||
* @return void
|
||||
*/
|
||||
protected function buildFile($list)
|
||||
{
|
||||
foreach ($list as $file) {
|
||||
if (!is_dir($this->basePath . dirname($file))) {
|
||||
// 创建目录
|
||||
mkdir($this->basePath . dirname($file), 0755, true);
|
||||
}
|
||||
|
||||
if (!is_file($this->basePath . $file)) {
|
||||
file_put_contents($this->basePath . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @param array $list build列表
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
public function module($module = '', $list = [], $namespace = 'app', $suffix = false)
|
||||
{
|
||||
$module = $module ? $module : '';
|
||||
|
||||
if (!is_dir($this->basePath . $module)) {
|
||||
// 创建模块目录
|
||||
mkdir($this->basePath . $module);
|
||||
}
|
||||
|
||||
if (basename($this->app->getRuntimePath()) != $module) {
|
||||
// 创建配置文件和公共文件
|
||||
$this->buildCommon($module);
|
||||
// 创建模块的默认页面
|
||||
$this->buildHello($module, $namespace, $suffix);
|
||||
}
|
||||
|
||||
if (empty($list)) {
|
||||
// 创建默认的模块目录和文件
|
||||
$list = [
|
||||
'__file__' => ['common.php'],
|
||||
'__dir__' => ['controller', 'model', 'view', 'config'],
|
||||
];
|
||||
}
|
||||
|
||||
// 创建子目录和文件
|
||||
foreach ($list as $path => $file) {
|
||||
$modulePath = $this->basePath . $module . DIRECTORY_SEPARATOR;
|
||||
if ('__dir__' == $path) {
|
||||
// 生成子目录
|
||||
foreach ($file as $dir) {
|
||||
$this->checkDirBuild($modulePath . $dir);
|
||||
}
|
||||
} elseif ('__file__' == $path) {
|
||||
// 生成(空白)文件
|
||||
foreach ($file as $name) {
|
||||
if (!is_file($modulePath . $name)) {
|
||||
file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 生成相关MVC文件
|
||||
foreach ($file as $val) {
|
||||
$val = trim($val);
|
||||
$filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . ($suffix ? ucfirst($path) : '') . '.php';
|
||||
$space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
|
||||
$class = $val . ($suffix ? ucfirst($path) : '');
|
||||
switch ($path) {
|
||||
case 'controller': // 控制器
|
||||
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
||||
break;
|
||||
case 'model': // 模型
|
||||
$content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
|
||||
break;
|
||||
case 'view': // 视图
|
||||
$filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . '.html';
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
$content = '';
|
||||
break;
|
||||
default:
|
||||
// 其他文件
|
||||
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
||||
}
|
||||
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注释自动生成路由规则
|
||||
* @access public
|
||||
* @param bool $suffix 类库后缀
|
||||
* @param string $layer 控制器层目录名
|
||||
* @return string
|
||||
*/
|
||||
public function buildRoute($suffix = false, $layer = '')
|
||||
{
|
||||
$namespace = $this->app->getNameSpace();
|
||||
$content = '<?php ' . PHP_EOL . '//根据 Annotation 自动生成的路由规则';
|
||||
|
||||
if (!$layer) {
|
||||
$layer = $this->app->config('app.url_controller_layer');
|
||||
}
|
||||
|
||||
if ($this->app->config('app.app_multi_module')) {
|
||||
$modules = glob($this->basePath . '*', GLOB_ONLYDIR);
|
||||
|
||||
foreach ($modules as $module) {
|
||||
$module = basename($module);
|
||||
|
||||
if (in_array($module, $this->app->config('app.deny_module_list'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR;
|
||||
$content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer);
|
||||
}
|
||||
} else {
|
||||
$path = $this->basePath . $layer . DIRECTORY_SEPARATOR;
|
||||
$content .= $this->buildDirRoute($path, $namespace, '', $suffix, $layer);
|
||||
}
|
||||
|
||||
$filename = $this->app->getRuntimePath() . 'build_route.php';
|
||||
file_put_contents($filename, $content);
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成子目录控制器类的路由规则
|
||||
* @access protected
|
||||
* @param string $path 控制器目录
|
||||
* @param string $namespace 应用命名空间
|
||||
* @param string $module 模块
|
||||
* @param bool $suffix 类库后缀
|
||||
* @param string $layer 控制器层目录名
|
||||
* @return string
|
||||
*/
|
||||
protected function buildDirRoute($path, $namespace, $module, $suffix, $layer)
|
||||
{
|
||||
$content = '';
|
||||
$controllers = glob($path . '*.php');
|
||||
|
||||
foreach ($controllers as $controller) {
|
||||
$controller = basename($controller, '.php');
|
||||
|
||||
$class = new \ReflectionClass($namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $controller);
|
||||
|
||||
if (strpos($layer, '\\')) {
|
||||
// 多级控制器
|
||||
$level = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11));
|
||||
$controller = $level . '.' . $controller;
|
||||
$length = strlen(strstr($layer, '\\', true));
|
||||
} else {
|
||||
$length = strlen($layer);
|
||||
}
|
||||
|
||||
if ($suffix) {
|
||||
$controller = substr($controller, 0, -$length);
|
||||
}
|
||||
|
||||
$content .= $this->getControllerRoute($class, $module, $controller);
|
||||
}
|
||||
|
||||
$subDir = glob($path . '*', GLOB_ONLYDIR);
|
||||
|
||||
foreach ($subDir as $dir) {
|
||||
$content .= $this->buildDirRoute($dir . DIRECTORY_SEPARATOR, $namespace, $module, $suffix, $layer . '\\' . basename($dir));
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成控制器类的路由规则
|
||||
* @access protected
|
||||
* @param string $class 控制器完整类名
|
||||
* @param string $module 模块名
|
||||
* @param string $controller 控制器名
|
||||
* @return string
|
||||
*/
|
||||
protected function getControllerRoute($class, $module, $controller)
|
||||
{
|
||||
$content = '';
|
||||
$comment = $class->getDocComment();
|
||||
|
||||
if (false !== strpos($comment, '@route(')) {
|
||||
$comment = $this->parseRouteComment($comment);
|
||||
$route = ($module ? $module . '/' : '') . $controller;
|
||||
$comment = preg_replace('/route\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::resource(\1,\'' . $route . '\')', $comment);
|
||||
$content .= PHP_EOL . $comment;
|
||||
} elseif (false !== strpos($comment, '@alias(')) {
|
||||
$comment = $this->parseRouteComment($comment, '@alias(');
|
||||
$route = ($module ? $module . '/' : '') . $controller;
|
||||
$comment = preg_replace('/alias\(\s?([\'\"][\-\_\/\w]+[\'\"])\s?\)/is', 'Route::alias(\1,\'' . $route . '\')', $comment);
|
||||
$content .= PHP_EOL . $comment;
|
||||
}
|
||||
|
||||
$methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$comment = $this->getMethodRouteComment($module, $controller, $method);
|
||||
if ($comment) {
|
||||
$content .= PHP_EOL . $comment;
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析路由注释
|
||||
* @access protected
|
||||
* @param string $comment
|
||||
* @param string $tag
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRouteComment($comment, $tag = '@route(')
|
||||
{
|
||||
$comment = substr($comment, 3, -2);
|
||||
$comment = explode(PHP_EOL, substr(strstr(trim($comment), $tag), 1));
|
||||
$comment = array_map(function ($item) {return trim(trim($item), ' \t*');}, $comment);
|
||||
|
||||
if (count($comment) > 1) {
|
||||
$key = array_search('', $comment);
|
||||
$comment = array_slice($comment, 0, false === $key ? 1 : $key);
|
||||
}
|
||||
|
||||
$comment = implode(PHP_EOL . "\t", $comment) . ';';
|
||||
|
||||
if (strpos($comment, '{')) {
|
||||
$comment = preg_replace_callback('/\{\s?.*?\s?\}/s', function ($matches) {
|
||||
return false !== strpos($matches[0], '"') ? '[' . substr(var_export(json_decode($matches[0], true), true), 7, -1) . ']' : $matches[0];
|
||||
}, $comment);
|
||||
}
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法的路由注释
|
||||
* @access protected
|
||||
* @param string $module 模块
|
||||
* @param string $controller 控制器名
|
||||
* @param \ReflectMethod $reflectMethod
|
||||
* @return string|void
|
||||
*/
|
||||
protected function getMethodRouteComment($module, $controller, $reflectMethod)
|
||||
{
|
||||
$comment = $reflectMethod->getDocComment();
|
||||
|
||||
if (false !== strpos($comment, '@route(')) {
|
||||
$comment = $this->parseRouteComment($comment);
|
||||
$action = $reflectMethod->getName();
|
||||
|
||||
if ($suffix = $this->app->config('app.action_suffix')) {
|
||||
$action = substr($action, 0, -strlen($suffix));
|
||||
}
|
||||
|
||||
$route = ($module ? $module . '/' : '') . $controller . '/' . $action;
|
||||
$comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\,?\s?[\'\"]?(\w+?)[\'\"]?\s?\)/is', 'Route::\2(\1,\'' . $route . '\')', $comment);
|
||||
$comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::rule(\1,\'' . $route . '\')', $comment);
|
||||
|
||||
return $comment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块的欢迎页面
|
||||
* @access protected
|
||||
* @param string $module 模块名
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
protected function buildHello($module, $namespace, $suffix = false)
|
||||
{
|
||||
$filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . ($suffix ? 'Controller' : '') . '.php';
|
||||
if (!is_file($filename)) {
|
||||
$content = file_get_contents($this->app->getThinkPath() . 'tpl' . DIRECTORY_SEPARATOR . 'default_index.tpl');
|
||||
$content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content);
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块的公共文件
|
||||
* @access protected
|
||||
* @param string $module 模块名
|
||||
* @return void
|
||||
*/
|
||||
protected function buildCommon($module)
|
||||
{
|
||||
$filename = $this->app->getConfigPath() . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'app.php';
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
|
||||
}
|
||||
|
||||
$filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'common.php';
|
||||
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, "<?php\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
* @access protected
|
||||
* @param string $dirname 目录名称
|
||||
* @return void
|
||||
*/
|
||||
protected function checkDirBuild($dirname)
|
||||
{
|
||||
if (!is_dir($dirname)) {
|
||||
mkdir($dirname, 0755, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Server/thinkphp/library/think/Cache.php
Normal file
133
Server/thinkphp/library/think/Cache.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Class Cache
|
||||
*
|
||||
* @package think
|
||||
*
|
||||
* @mixin Driver
|
||||
* @mixin \think\cache\driver\File
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
/**
|
||||
* 缓存实例
|
||||
* @var array
|
||||
*/
|
||||
protected $instance = [];
|
||||
|
||||
/**
|
||||
* 缓存配置
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 操作句柄
|
||||
* @var object
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->init($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接缓存
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @param bool|string $name 缓存连接标识 true 强制重新连接
|
||||
* @return Driver
|
||||
*/
|
||||
public function connect(array $options = [], $name = false)
|
||||
{
|
||||
if (false === $name) {
|
||||
$name = md5(serialize($options));
|
||||
}
|
||||
|
||||
if (true === $name || !isset($this->instance[$name])) {
|
||||
$type = !empty($options['type']) ? $options['type'] : 'File';
|
||||
|
||||
if (true === $name) {
|
||||
$name = md5(serialize($options));
|
||||
}
|
||||
|
||||
$this->instance[$name] = Loader::factory($type, '\\think\\cache\\driver\\', $options);
|
||||
}
|
||||
|
||||
return $this->instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动初始化缓存
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @param bool $force 强制更新
|
||||
* @return Driver
|
||||
*/
|
||||
public function init(array $options = [], $force = false)
|
||||
{
|
||||
if (is_null($this->handler) || $force) {
|
||||
|
||||
if ('complex' == $options['type']) {
|
||||
$default = $options['default'];
|
||||
$options = isset($options[$default['type']]) ? $options[$default['type']] : $default;
|
||||
}
|
||||
|
||||
$this->handler = $this->connect($options);
|
||||
}
|
||||
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
public static function __make(Config $config)
|
||||
{
|
||||
return new static($config->pull('cache'));
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换缓存类型 需要配置 cache.type 为 complex
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @return Driver
|
||||
*/
|
||||
public function store($name = '')
|
||||
{
|
||||
if ('' !== $name && 'complex' == $this->config['type']) {
|
||||
return $this->connect($this->config[$name], strtolower($name));
|
||||
}
|
||||
|
||||
return $this->init();
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array([$this->init(), $method], $args);
|
||||
}
|
||||
|
||||
}
|
||||
552
Server/thinkphp/library/think/Collection.php
Normal file
552
Server/thinkphp/library/think/Collection.php
Normal file
@@ -0,0 +1,552 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
|
||||
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
/**
|
||||
* 数据集数据
|
||||
* @var array
|
||||
*/
|
||||
protected $items = [];
|
||||
|
||||
public function __construct($items = [])
|
||||
{
|
||||
$this->items = $this->convertToArray($items);
|
||||
}
|
||||
|
||||
public static function make($items = [])
|
||||
{
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->items);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function ($value) {
|
||||
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
|
||||
}, $this->items);
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并数组
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $items
|
||||
* @return static
|
||||
*/
|
||||
public function merge($items)
|
||||
{
|
||||
return new static(array_merge($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 交换数组中的键和值
|
||||
*
|
||||
* @access public
|
||||
* @return static
|
||||
*/
|
||||
public function flip()
|
||||
{
|
||||
return new static(array_flip($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定键整理数据
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $items 数据
|
||||
* @param string $indexKey 键名
|
||||
* @return array
|
||||
*/
|
||||
public function dictionary($items = null, &$indexKey = null)
|
||||
{
|
||||
if ($items instanceof self || $items instanceof Paginator) {
|
||||
$items = $items->all();
|
||||
}
|
||||
|
||||
$items = is_null($items) ? $this->items : $items;
|
||||
|
||||
if ($items && empty($indexKey)) {
|
||||
$indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
|
||||
}
|
||||
|
||||
if (isset($indexKey) && is_string($indexKey)) {
|
||||
return array_column($items, null, $indexKey);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较数组,返回差集
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $items 数据
|
||||
* @param string $indexKey 指定比较的键名
|
||||
* @return static
|
||||
*/
|
||||
public function diff($items, $indexKey = null)
|
||||
{
|
||||
if ($this->isEmpty() || is_scalar($this->items[0])) {
|
||||
return new static(array_diff($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
$diff = [];
|
||||
$dictionary = $this->dictionary($items, $indexKey);
|
||||
|
||||
if (is_string($indexKey)) {
|
||||
foreach ($this->items as $item) {
|
||||
if (!isset($dictionary[$item[$indexKey]])) {
|
||||
$diff[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new static($diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较数组,返回交集
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $items 数据
|
||||
* @param string $indexKey 指定比较的键名
|
||||
* @return static
|
||||
*/
|
||||
public function intersect($items, $indexKey = null)
|
||||
{
|
||||
if ($this->isEmpty() || is_scalar($this->items[0])) {
|
||||
return new static(array_diff($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
$intersect = [];
|
||||
$dictionary = $this->dictionary($items, $indexKey);
|
||||
|
||||
if (is_string($indexKey)) {
|
||||
foreach ($this->items as $item) {
|
||||
if (isset($dictionary[$item[$indexKey]])) {
|
||||
$intersect[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new static($intersect);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中所有的键名
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function keys()
|
||||
{
|
||||
$current = current($this->items);
|
||||
|
||||
if (is_scalar($current)) {
|
||||
$array = $this->items;
|
||||
} elseif (is_array($current)) {
|
||||
$array = $current;
|
||||
} else {
|
||||
$array = $current->toArray();
|
||||
}
|
||||
|
||||
return array_keys($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数组的最后一个元素(出栈)
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function pop()
|
||||
{
|
||||
return array_pop($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过使用用户自定义函数,以字符串返回数组
|
||||
*
|
||||
* @access public
|
||||
* @param callable $callback
|
||||
* @param mixed $initial
|
||||
* @return mixed
|
||||
*/
|
||||
public function reduce(callable $callback, $initial = null)
|
||||
{
|
||||
return array_reduce($this->items, $callback, $initial);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以相反的顺序返回数组。
|
||||
*
|
||||
* @access public
|
||||
* @return static
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
return new static(array_reverse($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数组中首个元素,并返回被删除元素的值
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function shift()
|
||||
{
|
||||
return array_shift($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在数组结尾插入一个元素
|
||||
* @access public
|
||||
* @param mixed $value
|
||||
* @param mixed $key
|
||||
* @return void
|
||||
*/
|
||||
public function push($value, $key = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一个数组分割为新的数组块.
|
||||
*
|
||||
* @access public
|
||||
* @param int $size
|
||||
* @param bool $preserveKeys
|
||||
* @return static
|
||||
*/
|
||||
public function chunk($size, $preserveKeys = false)
|
||||
{
|
||||
$chunks = [];
|
||||
|
||||
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
|
||||
$chunks[] = new static($chunk);
|
||||
}
|
||||
|
||||
return new static($chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在数组开头插入一个元素
|
||||
* @access public
|
||||
* @param mixed $value
|
||||
* @param mixed $key
|
||||
* @return void
|
||||
*/
|
||||
public function unshift($value, $key = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
array_unshift($this->items, $value);
|
||||
} else {
|
||||
$this->items = [$key => $value] + $this->items;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给每个元素执行个回调
|
||||
*
|
||||
* @access public
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function each(callable $callback)
|
||||
{
|
||||
foreach ($this->items as $key => $item) {
|
||||
$result = $callback($item, $key);
|
||||
|
||||
if (false === $result) {
|
||||
break;
|
||||
} elseif (!is_object($item)) {
|
||||
$this->items[$key] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用回调函数处理数组中的元素
|
||||
* @access public
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function map(callable $callback)
|
||||
{
|
||||
return new static(array_map($callback, $this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用回调函数过滤数组中的元素
|
||||
* @access public
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function filter(callable $callback = null)
|
||||
{
|
||||
if ($callback) {
|
||||
return new static(array_filter($this->items, $callback));
|
||||
}
|
||||
|
||||
return new static(array_filter($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段条件过滤数组中的元素
|
||||
* @access public
|
||||
* @param string $field 字段名
|
||||
* @param mixed $operator 操作符
|
||||
* @param mixed $value 数据
|
||||
* @return static
|
||||
*/
|
||||
public function where($field, $operator, $value = null)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
$value = $operator;
|
||||
$operator = '=';
|
||||
}
|
||||
|
||||
return $this->filter(function ($data) use ($field, $operator, $value) {
|
||||
if (strpos($field, '.')) {
|
||||
list($field, $relation) = explode('.', $field);
|
||||
|
||||
$result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
|
||||
} else {
|
||||
$result = isset($data[$field]) ? $data[$field] : null;
|
||||
}
|
||||
|
||||
switch (strtolower($operator)) {
|
||||
case '===':
|
||||
return $result === $value;
|
||||
case '!==':
|
||||
return $result !== $value;
|
||||
case '!=':
|
||||
case '<>':
|
||||
return $result != $value;
|
||||
case '>':
|
||||
return $result > $value;
|
||||
case '>=':
|
||||
return $result >= $value;
|
||||
case '<':
|
||||
return $result < $value;
|
||||
case '<=':
|
||||
return $result <= $value;
|
||||
case 'like':
|
||||
return is_string($result) && false !== strpos($result, $value);
|
||||
case 'not like':
|
||||
return is_string($result) && false === strpos($result, $value);
|
||||
case 'in':
|
||||
return is_scalar($result) && in_array($result, $value, true);
|
||||
case 'not in':
|
||||
return is_scalar($result) && !in_array($result, $value, true);
|
||||
case 'between':
|
||||
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
|
||||
return is_scalar($result) && $result >= $min && $result <= $max;
|
||||
case 'not between':
|
||||
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
|
||||
return is_scalar($result) && $result > $max || $result < $min;
|
||||
case '==':
|
||||
case '=':
|
||||
default:
|
||||
return $result == $value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据中指定的一列
|
||||
* @access public
|
||||
* @param mixed $columnKey 键名
|
||||
* @param mixed $indexKey 作为索引值的列
|
||||
* @return array
|
||||
*/
|
||||
public function column($columnKey, $indexKey = null)
|
||||
{
|
||||
return array_column($this->toArray(), $columnKey, $indexKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对数组排序
|
||||
*
|
||||
* @access public
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function sort(callable $callback = null)
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
$callback = $callback ?: function ($a, $b) {
|
||||
return $a == $b ? 0 : (($a < $b) ? -1 : 1);
|
||||
|
||||
};
|
||||
|
||||
uasort($items, $callback);
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定字段排序
|
||||
* @access public
|
||||
* @param string $field 排序字段
|
||||
* @param string $order 排序
|
||||
* @param bool $intSort 是否为数字排序
|
||||
* @return $this
|
||||
*/
|
||||
public function order($field, $order = null, $intSort = true)
|
||||
{
|
||||
return $this->sort(function ($a, $b) use ($field, $order, $intSort) {
|
||||
$fieldA = isset($a[$field]) ? $a[$field] : null;
|
||||
$fieldB = isset($b[$field]) ? $b[$field] : null;
|
||||
|
||||
if ($intSort) {
|
||||
return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB;
|
||||
} else {
|
||||
return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数组打乱
|
||||
*
|
||||
* @access public
|
||||
* @return static
|
||||
*/
|
||||
public function shuffle()
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
shuffle($items);
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取数组
|
||||
*
|
||||
* @access public
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @param bool $preserveKeys
|
||||
* @return static
|
||||
*/
|
||||
public function slice($offset, $length = null, $preserveKeys = false)
|
||||
{
|
||||
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
|
||||
}
|
||||
|
||||
// ArrayAccess
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->items);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
//Countable
|
||||
public function count()
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
//IteratorAggregate
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->items);
|
||||
}
|
||||
|
||||
//JsonSerializable
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换当前数据集为JSON字符串
|
||||
* @access public
|
||||
* @param integer $options json参数
|
||||
* @return string
|
||||
*/
|
||||
public function toJson($options = JSON_UNESCAPED_UNICODE)
|
||||
{
|
||||
return json_encode($this->toArray(), $options);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成数组
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $items
|
||||
* @return array
|
||||
*/
|
||||
protected function convertToArray($items)
|
||||
{
|
||||
if ($items instanceof self) {
|
||||
return $items->all();
|
||||
}
|
||||
|
||||
return (array) $items;
|
||||
}
|
||||
}
|
||||
398
Server/thinkphp/library/think/Config.php
Normal file
398
Server/thinkphp/library/think/Config.php
Normal file
@@ -0,0 +1,398 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use Yaconf;
|
||||
|
||||
class Config implements \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 配置前缀
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix = 'app';
|
||||
|
||||
/**
|
||||
* 配置文件目录
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* 配置文件后缀
|
||||
* @var string
|
||||
*/
|
||||
protected $ext;
|
||||
|
||||
/**
|
||||
* 是否支持Yaconf
|
||||
* @var bool
|
||||
*/
|
||||
protected $yaconf;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($path = '', $ext = '.php')
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->ext = $ext;
|
||||
$this->yaconf = class_exists('Yaconf');
|
||||
}
|
||||
|
||||
public static function __make(App $app)
|
||||
{
|
||||
$path = $app->getConfigPath();
|
||||
$ext = $app->getConfigExt();
|
||||
return new static($path, $ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置开启Yaconf
|
||||
* @access public
|
||||
* @param bool|string $yaconf 是否使用Yaconf
|
||||
* @return void
|
||||
*/
|
||||
public function setYaconf($yaconf)
|
||||
{
|
||||
if ($this->yaconf) {
|
||||
$this->yaconf = $yaconf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置参数默认前缀
|
||||
* @access public
|
||||
* @param string $prefix 前缀
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultPrefix($prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析配置文件或内容
|
||||
* @access public
|
||||
* @param string $config 配置文件路径或内容
|
||||
* @param string $type 配置解析类型
|
||||
* @param string $name 配置名(如设置即表示二级配置)
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($config, $type = '', $name = '')
|
||||
{
|
||||
if (empty($type)) {
|
||||
$type = pathinfo($config, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
$object = Loader::factory($type, '\\think\\config\\driver\\', $config);
|
||||
|
||||
return $this->set($object->parse(), $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置文件(多种格式)
|
||||
* @access public
|
||||
* @param string $file 配置文件名
|
||||
* @param string $name 一级配置名
|
||||
* @return mixed
|
||||
*/
|
||||
public function load($file, $name = '')
|
||||
{
|
||||
if (is_file($file)) {
|
||||
$filename = $file;
|
||||
} elseif (is_file($this->path . $file . $this->ext)) {
|
||||
$filename = $this->path . $file . $this->ext;
|
||||
}
|
||||
|
||||
if (isset($filename)) {
|
||||
return $this->loadFile($filename, $name);
|
||||
} elseif ($this->yaconf && Yaconf::has($file)) {
|
||||
return $this->set(Yaconf::get($file), $name);
|
||||
}
|
||||
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的yaconf配置参数
|
||||
* @access protected
|
||||
* @param string $name 配置参数名
|
||||
* @return string
|
||||
*/
|
||||
protected function getYaconfName($name)
|
||||
{
|
||||
if ($this->yaconf && is_string($this->yaconf)) {
|
||||
return $this->yaconf . '.' . $name;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取yaconf配置
|
||||
* @access public
|
||||
* @param string $name 配置参数名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function yaconf($name, $default = null)
|
||||
{
|
||||
if ($this->yaconf) {
|
||||
$yaconfName = $this->getYaconfName($name);
|
||||
|
||||
if (Yaconf::has($yaconfName)) {
|
||||
return Yaconf::get($yaconfName);
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
protected function loadFile($file, $name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$type = pathinfo($file, PATHINFO_EXTENSION);
|
||||
|
||||
if ('php' == $type) {
|
||||
return $this->set(include $file, $name);
|
||||
} elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
|
||||
return $this->set(yaml_parse_file($file), $name);
|
||||
}
|
||||
|
||||
return $this->parse($file, $type, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测配置是否存在
|
||||
* @access public
|
||||
* @param string $name 配置参数名(支持多级配置 .号分割)
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
if (false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
return !is_null($this->get($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一级配置
|
||||
* @access public
|
||||
* @param string $name 一级配置名
|
||||
* @return array
|
||||
*/
|
||||
public function pull($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
if ($this->yaconf) {
|
||||
$yaconfName = $this->getYaconfName($name);
|
||||
|
||||
if (Yaconf::has($yaconfName)) {
|
||||
$config = Yaconf::get($yaconfName);
|
||||
return isset($this->config[$name]) ? array_merge($this->config[$name], $config) : $config;
|
||||
}
|
||||
}
|
||||
|
||||
return isset($this->config[$name]) ? $this->config[$name] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置参数 为空则获取所有配置
|
||||
* @access public
|
||||
* @param string $name 配置参数名(支持多级配置 .号分割)
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name = null, $default = null)
|
||||
{
|
||||
if ($name && false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
// 无参数时获取所有
|
||||
if (empty($name)) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
if ('.' == substr($name, -1)) {
|
||||
return $this->pull(substr($name, 0, -1));
|
||||
}
|
||||
|
||||
if ($this->yaconf) {
|
||||
$yaconfName = $this->getYaconfName($name);
|
||||
|
||||
if (Yaconf::has($yaconfName)) {
|
||||
return Yaconf::get($yaconfName);
|
||||
}
|
||||
}
|
||||
|
||||
$name = explode('.', $name);
|
||||
$name[0] = strtolower($name[0]);
|
||||
$config = $this->config;
|
||||
|
||||
// 按.拆分成多维数组进行判断
|
||||
foreach ($name as $val) {
|
||||
if (isset($config[$val])) {
|
||||
$config = $config[$val];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置参数 name为数组则为批量设置
|
||||
* @access public
|
||||
* @param string|array $name 配置参数名(支持三级配置 .号分割)
|
||||
* @param mixed $value 配置值
|
||||
* @return mixed
|
||||
*/
|
||||
public function set($name, $value = null)
|
||||
{
|
||||
if (is_string($name)) {
|
||||
if (false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
$name = explode('.', $name, 3);
|
||||
|
||||
if (count($name) == 2) {
|
||||
$this->config[strtolower($name[0])][$name[1]] = $value;
|
||||
} else {
|
||||
$this->config[strtolower($name[0])][$name[1]][$name[2]] = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
} elseif (is_array($name)) {
|
||||
// 批量设置
|
||||
if (!empty($value)) {
|
||||
if (isset($this->config[$value])) {
|
||||
$result = array_merge($this->config[$value], $name);
|
||||
} else {
|
||||
$result = $name;
|
||||
}
|
||||
|
||||
$this->config[$value] = $result;
|
||||
} else {
|
||||
$result = $this->config = array_merge($this->config, $name);
|
||||
}
|
||||
} else {
|
||||
// 为空直接返回 已有配置
|
||||
$result = $this->config;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除配置
|
||||
* @access public
|
||||
* @param string $name 配置参数名(支持三级配置 .号分割)
|
||||
* @return void
|
||||
*/
|
||||
public function remove($name)
|
||||
{
|
||||
if (false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
$name = explode('.', $name, 3);
|
||||
|
||||
if (count($name) == 2) {
|
||||
unset($this->config[strtolower($name[0])][$name[1]]);
|
||||
} else {
|
||||
unset($this->config[strtolower($name[0])][$name[1]][$name[2]]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置配置参数
|
||||
* @access public
|
||||
* @param string $prefix 配置前缀名
|
||||
* @return void
|
||||
*/
|
||||
public function reset($prefix = '')
|
||||
{
|
||||
if ('' === $prefix) {
|
||||
$this->config = [];
|
||||
} else {
|
||||
$this->config[$prefix] = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @access public
|
||||
* @param string $name 参数名
|
||||
* @param mixed $value 值
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
return $this->set($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置参数
|
||||
* @access public
|
||||
* @param string $name 参数名
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否存在参数
|
||||
* @access public
|
||||
* @param string $name 参数名
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return $this->has($name);
|
||||
}
|
||||
|
||||
// ArrayAccess
|
||||
public function offsetSet($name, $value)
|
||||
{
|
||||
$this->set($name, $value);
|
||||
}
|
||||
|
||||
public function offsetExists($name)
|
||||
{
|
||||
return $this->has($name);
|
||||
}
|
||||
|
||||
public function offsetUnset($name)
|
||||
{
|
||||
$this->remove($name);
|
||||
}
|
||||
|
||||
public function offsetGet($name)
|
||||
{
|
||||
return $this->get($name);
|
||||
}
|
||||
}
|
||||
829
Server/thinkphp/library/think/Console.php
Normal file
829
Server/thinkphp/library/think/Console.php
Normal file
@@ -0,0 +1,829 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | TopThink [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\command\Help as HelpCommand;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument as InputArgument;
|
||||
use think\console\input\Definition as InputDefinition;
|
||||
use think\console\input\Option as InputOption;
|
||||
use think\console\Output;
|
||||
use think\console\output\driver\Buffer;
|
||||
|
||||
class Console
|
||||
{
|
||||
|
||||
private $name;
|
||||
private $version;
|
||||
|
||||
/** @var Command[] */
|
||||
private $commands = [];
|
||||
|
||||
private $wantHelps = false;
|
||||
|
||||
private $catchExceptions = true;
|
||||
private $autoExit = true;
|
||||
private $definition;
|
||||
private $defaultCommand;
|
||||
|
||||
private static $defaultCommands = [
|
||||
'help' => "think\\console\\command\\Help",
|
||||
'list' => "think\\console\\command\\Lists",
|
||||
'build' => "think\\console\\command\\Build",
|
||||
'clear' => "think\\console\\command\\Clear",
|
||||
'make:command' => "think\\console\\command\\make\\Command",
|
||||
'make:controller' => "think\\console\\command\\make\\Controller",
|
||||
'make:model' => "think\\console\\command\\make\\Model",
|
||||
'make:middleware' => "think\\console\\command\\make\\Middleware",
|
||||
'make:validate' => "think\\console\\command\\make\\Validate",
|
||||
'optimize:autoload' => "think\\console\\command\\optimize\\Autoload",
|
||||
'optimize:config' => "think\\console\\command\\optimize\\Config",
|
||||
'optimize:schema' => "think\\console\\command\\optimize\\Schema",
|
||||
'optimize:route' => "think\\console\\command\\optimize\\Route",
|
||||
'run' => "think\\console\\command\\RunServer",
|
||||
'version' => "think\\console\\command\\Version",
|
||||
'route:list' => "think\\console\\command\\RouteList",
|
||||
];
|
||||
|
||||
/**
|
||||
* Console constructor.
|
||||
* @access public
|
||||
* @param string $name 名称
|
||||
* @param string $version 版本
|
||||
* @param null|string $user 执行用户
|
||||
*/
|
||||
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->version = $version;
|
||||
|
||||
if ($user) {
|
||||
$this->setUser($user);
|
||||
}
|
||||
|
||||
$this->defaultCommand = 'list';
|
||||
$this->definition = $this->getDefaultInputDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置执行用户
|
||||
* @param $user
|
||||
*/
|
||||
public function setUser($user)
|
||||
{
|
||||
if (DIRECTORY_SEPARATOR == '\\') {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = posix_getpwnam($user);
|
||||
if ($user) {
|
||||
posix_setuid($user['uid']);
|
||||
posix_setgid($user['gid']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 Console
|
||||
* @access public
|
||||
* @param bool $run 是否运行 Console
|
||||
* @return int|Console
|
||||
*/
|
||||
public static function init($run = true)
|
||||
{
|
||||
static $console;
|
||||
|
||||
if (!$console) {
|
||||
$config = Container::get('config')->pull('console');
|
||||
$console = new self($config['name'], $config['version'], $config['user']);
|
||||
|
||||
$commands = $console->getDefinedCommands($config);
|
||||
|
||||
// 添加指令集
|
||||
$console->addCommands($commands);
|
||||
}
|
||||
|
||||
if ($run) {
|
||||
// 运行
|
||||
return $console->run();
|
||||
} else {
|
||||
return $console;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
public function getDefinedCommands(array $config = [])
|
||||
{
|
||||
$commands = self::$defaultCommands;
|
||||
|
||||
if (!empty($config['auto_path']) && is_dir($config['auto_path'])) {
|
||||
// 自动加载指令类
|
||||
$files = scandir($config['auto_path']);
|
||||
|
||||
if (count($files) > 2) {
|
||||
$beforeClass = get_declared_classes();
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (pathinfo($file, PATHINFO_EXTENSION) == 'php') {
|
||||
include $config['auto_path'] . $file;
|
||||
}
|
||||
}
|
||||
|
||||
$afterClass = get_declared_classes();
|
||||
$commands = array_merge($commands, array_diff($afterClass, $beforeClass));
|
||||
}
|
||||
}
|
||||
|
||||
$file = Container::get('env')->get('app_path') . 'command.php';
|
||||
|
||||
if (is_file($file)) {
|
||||
$appCommands = include $file;
|
||||
|
||||
if (is_array($appCommands)) {
|
||||
$commands = array_merge($commands, $appCommands);
|
||||
}
|
||||
}
|
||||
|
||||
return $commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @param string $command
|
||||
* @param array $parameters
|
||||
* @param string $driver
|
||||
* @return Output|Buffer
|
||||
*/
|
||||
public static function call($command, array $parameters = [], $driver = 'buffer')
|
||||
{
|
||||
$console = self::init(false);
|
||||
|
||||
array_unshift($parameters, $command);
|
||||
|
||||
$input = new Input($parameters);
|
||||
$output = new Output($driver);
|
||||
|
||||
$console->setCatchExceptions(false);
|
||||
$console->find($command)->run($input, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行当前的指令
|
||||
* @access public
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
* @api
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$input = new Input();
|
||||
$output = new Output();
|
||||
|
||||
$this->configureIO($input, $output);
|
||||
|
||||
try {
|
||||
$exitCode = $this->doRun($input, $output);
|
||||
} catch (\Exception $e) {
|
||||
if (!$this->catchExceptions) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$output->renderException($e);
|
||||
|
||||
$exitCode = $e->getCode();
|
||||
if (is_numeric($exitCode)) {
|
||||
$exitCode = (int) $exitCode;
|
||||
if (0 === $exitCode) {
|
||||
$exitCode = 1;
|
||||
}
|
||||
} else {
|
||||
$exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->autoExit) {
|
||||
if ($exitCode > 255) {
|
||||
$exitCode = 255;
|
||||
}
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @access public
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return int
|
||||
*/
|
||||
public function doRun(Input $input, Output $output)
|
||||
{
|
||||
if (true === $input->hasParameterOption(['--version', '-V'])) {
|
||||
$output->writeln($this->getLongVersion());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$name = $this->getCommandName($input);
|
||||
|
||||
if (true === $input->hasParameterOption(['--help', '-h'])) {
|
||||
if (!$name) {
|
||||
$name = 'help';
|
||||
$input = new Input(['help']);
|
||||
} else {
|
||||
$this->wantHelps = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$name) {
|
||||
$name = $this->defaultCommand;
|
||||
$input = new Input([$this->defaultCommand]);
|
||||
}
|
||||
|
||||
$command = $this->find($name);
|
||||
|
||||
$exitCode = $this->doRunCommand($command, $input, $output);
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入参数定义
|
||||
* @access public
|
||||
* @param InputDefinition $definition
|
||||
*/
|
||||
public function setDefinition(InputDefinition $definition)
|
||||
{
|
||||
$this->definition = $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入参数定义
|
||||
* @access public
|
||||
* @return InputDefinition The InputDefinition instance
|
||||
*/
|
||||
public function getDefinition()
|
||||
{
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the help message.
|
||||
* @access public
|
||||
* @return string A help message.
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
return $this->getLongVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否捕获异常
|
||||
* @access public
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setCatchExceptions($boolean)
|
||||
{
|
||||
$this->catchExceptions = (bool) $boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否自动退出
|
||||
* @access public
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setAutoExit($boolean)
|
||||
{
|
||||
$this->autoExit = (bool) $boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取名称
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置名称
|
||||
* @access public
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本
|
||||
* @access public
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置版本
|
||||
* @access public
|
||||
* @param string $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的版本号
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getLongVersion()
|
||||
{
|
||||
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
|
||||
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
|
||||
}
|
||||
|
||||
return '<info>Console Tool</info>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个指令 (便于动态创建指令)
|
||||
* @access public
|
||||
* @param string $name 指令名
|
||||
* @return Command
|
||||
*/
|
||||
public function register($name)
|
||||
{
|
||||
return $this->add(new Command($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指令集
|
||||
* @access public
|
||||
* @param array $commands
|
||||
*/
|
||||
public function addCommands(array $commands)
|
||||
{
|
||||
foreach ($commands as $key => $command) {
|
||||
if (is_subclass_of($command, "\\think\\console\\Command")) {
|
||||
// 注册指令
|
||||
$this->add($command, is_numeric($key) ? '' : $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个指令(对象)
|
||||
* @access public
|
||||
* @param mixed $command 指令对象或者指令类名
|
||||
* @param string $name 指令名 留空则自动获取
|
||||
* @return mixed
|
||||
*/
|
||||
public function add($command, $name)
|
||||
{
|
||||
if ($name) {
|
||||
$this->commands[$name] = $command;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_string($command)) {
|
||||
$command = new $command();
|
||||
}
|
||||
|
||||
$command->setConsole($this);
|
||||
|
||||
if (!$command->isEnabled()) {
|
||||
$command->setConsole(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $command->getDefinition()) {
|
||||
throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
|
||||
}
|
||||
|
||||
$this->commands[$command->getName()] = $command;
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$this->commands[$alias] = $command;
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令
|
||||
* @access public
|
||||
* @param string $name 指令名称
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
if (!isset($this->commands[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
$command = $this->commands[$name];
|
||||
|
||||
if (is_string($command)) {
|
||||
$command = new $command();
|
||||
}
|
||||
|
||||
$command->setConsole($this);
|
||||
|
||||
if ($this->wantHelps) {
|
||||
$this->wantHelps = false;
|
||||
|
||||
/** @var HelpCommand $helpCommand */
|
||||
$helpCommand = $this->get('help');
|
||||
$helpCommand->setCommand($command);
|
||||
|
||||
return $helpCommand;
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 某个指令是否存在
|
||||
* @access public
|
||||
* @param string $name 指令名称
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return isset($this->commands[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的命名空间
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getNamespaces()
|
||||
{
|
||||
$namespaces = [];
|
||||
foreach ($this->commands as $name => $command) {
|
||||
if (is_string($command)) {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($name));
|
||||
} else {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter($namespaces)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找注册命名空间中的名称或缩写。
|
||||
* @access public
|
||||
* @param string $namespace
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function findNamespace($namespace)
|
||||
{
|
||||
$allNamespaces = $this->getNamespaces();
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $namespace);
|
||||
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
|
||||
|
||||
if (empty($namespaces)) {
|
||||
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
|
||||
if (1 == count($alternatives)) {
|
||||
$message .= "\n\nDid you mean this?\n ";
|
||||
} else {
|
||||
$message .= "\n\nDid you mean one of these?\n ";
|
||||
}
|
||||
|
||||
$message .= implode("\n ", $alternatives);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
$exact = in_array($namespace, $namespaces, true);
|
||||
if (count($namespaces) > 1 && !$exact) {
|
||||
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
|
||||
}
|
||||
|
||||
return $exact ? $namespace : reset($namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指令
|
||||
* @access public
|
||||
* @param string $name 名称或者别名
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function find($name)
|
||||
{
|
||||
$allCommands = array_keys($this->commands);
|
||||
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $name);
|
||||
|
||||
$commands = preg_grep('{^' . $expr . '}', $allCommands);
|
||||
|
||||
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
|
||||
if (false !== $pos = strrpos($name, ':')) {
|
||||
$this->findNamespace(substr($name, 0, $pos));
|
||||
}
|
||||
|
||||
$message = sprintf('Command "%s" is not defined.', $name);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
|
||||
if (1 == count($alternatives)) {
|
||||
$message .= "\n\nDid you mean this?\n ";
|
||||
} else {
|
||||
$message .= "\n\nDid you mean one of these?\n ";
|
||||
}
|
||||
$message .= implode("\n ", $alternatives);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
$exact = in_array($name, $commands, true);
|
||||
if (count($commands) > 1 && !$exact) {
|
||||
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
|
||||
}
|
||||
|
||||
return $this->get($exact ? $name : reset($commands));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的指令
|
||||
* @access public
|
||||
* @param string $namespace 命名空间
|
||||
* @return Command[]
|
||||
* @api
|
||||
*/
|
||||
public function all($namespace = null)
|
||||
{
|
||||
if (null === $namespace) {
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
$commands = [];
|
||||
foreach ($this->commands as $name => $command) {
|
||||
if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
|
||||
$commands[$name] = $command;
|
||||
}
|
||||
}
|
||||
|
||||
return $commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可能的指令名
|
||||
* @access public
|
||||
* @param array $names
|
||||
* @return array
|
||||
*/
|
||||
public static function getAbbreviations($names)
|
||||
{
|
||||
$abbrevs = [];
|
||||
foreach ($names as $name) {
|
||||
for ($len = strlen($name); $len > 0; --$len) {
|
||||
$abbrev = substr($name, 0, $len);
|
||||
$abbrevs[$abbrev][] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $abbrevs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置基于用户的参数和选项的输入和输出实例。
|
||||
* @access protected
|
||||
* @param Input $input 输入实例
|
||||
* @param Output $output 输出实例
|
||||
*/
|
||||
protected function configureIO(Input $input, Output $output)
|
||||
{
|
||||
if (true === $input->hasParameterOption(['--ansi'])) {
|
||||
$output->setDecorated(true);
|
||||
} elseif (true === $input->hasParameterOption(['--no-ansi'])) {
|
||||
$output->setDecorated(false);
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
|
||||
$input->setInteractive(false);
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(['--quiet', '-q'])) {
|
||||
$output->setVerbosity(Output::VERBOSITY_QUIET);
|
||||
} else {
|
||||
if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
|
||||
$output->setVerbosity(Output::VERBOSITY_DEBUG);
|
||||
} elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
|
||||
$output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
|
||||
} elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
|
||||
$output->setVerbosity(Output::VERBOSITY_VERBOSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @access protected
|
||||
* @param Command $command 指令实例
|
||||
* @param Input $input 输入实例
|
||||
* @param Output $output 输出实例
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function doRunCommand(Command $command, Input $input, Output $output)
|
||||
{
|
||||
return $command->run($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令的基础名称
|
||||
* @access protected
|
||||
* @param Input $input
|
||||
* @return string
|
||||
*/
|
||||
protected function getCommandName(Input $input)
|
||||
{
|
||||
return $input->getFirstArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认输入定义
|
||||
* @access protected
|
||||
* @return InputDefinition
|
||||
*/
|
||||
protected function getDefaultInputDefinition()
|
||||
{
|
||||
return new InputDefinition([
|
||||
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
|
||||
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
|
||||
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
|
||||
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
|
||||
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
|
||||
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
|
||||
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
|
||||
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function addDefaultCommands(array $classnames)
|
||||
{
|
||||
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可能的建议
|
||||
* @access private
|
||||
* @param array $abbrevs
|
||||
* @return string
|
||||
*/
|
||||
private function getAbbreviationSuggestions($abbrevs)
|
||||
{
|
||||
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命名空间部分
|
||||
* @access public
|
||||
* @param string $name 指令
|
||||
* @param string $limit 部分的命名空间的最大数量
|
||||
* @return string
|
||||
*/
|
||||
public function extractNamespace($name, $limit = null)
|
||||
{
|
||||
$parts = explode(':', $name);
|
||||
array_pop($parts);
|
||||
|
||||
return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找可替代的建议
|
||||
* @access private
|
||||
* @param string $name
|
||||
* @param array|\Traversable $collection
|
||||
* @return array
|
||||
*/
|
||||
private function findAlternatives($name, $collection)
|
||||
{
|
||||
$threshold = 1e3;
|
||||
$alternatives = [];
|
||||
|
||||
$collectionParts = [];
|
||||
foreach ($collection as $item) {
|
||||
$collectionParts[$item] = explode(':', $item);
|
||||
}
|
||||
|
||||
foreach (explode(':', $name) as $i => $subname) {
|
||||
foreach ($collectionParts as $collectionName => $parts) {
|
||||
$exists = isset($alternatives[$collectionName]);
|
||||
if (!isset($parts[$i]) && $exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
continue;
|
||||
} elseif (!isset($parts[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lev = levenshtein($subname, $parts[$i]);
|
||||
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
|
||||
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
|
||||
} elseif ($exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$lev = levenshtein($name, $item);
|
||||
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
|
||||
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
|
||||
}
|
||||
}
|
||||
|
||||
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
|
||||
return $lev < 2 * $threshold;
|
||||
});
|
||||
asort($alternatives);
|
||||
|
||||
return array_keys($alternatives);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认的指令
|
||||
* @access public
|
||||
* @param string $commandName The Command name
|
||||
*/
|
||||
public function setDefaultCommand($commandName)
|
||||
{
|
||||
$this->defaultCommand = $commandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有的命名空间
|
||||
* @access private
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
private function extractAllNamespaces($name)
|
||||
{
|
||||
$parts = explode(':', $name, -1);
|
||||
$namespaces = [];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (count($namespaces)) {
|
||||
$namespaces[] = end($namespaces) . ':' . $part;
|
||||
} else {
|
||||
$namespaces[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['commands'], $data['definition']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
618
Server/thinkphp/library/think/Container.php
Normal file
618
Server/thinkphp/library/think/Container.php
Normal file
@@ -0,0 +1,618 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Closure;
|
||||
use Countable;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
use think\exception\ClassNotFoundException;
|
||||
|
||||
/**
|
||||
* @package think
|
||||
* @property Build $build
|
||||
* @property Cache $cache
|
||||
* @property Config $config
|
||||
* @property Cookie $cookie
|
||||
* @property Debug $debug
|
||||
* @property Env $env
|
||||
* @property Hook $hook
|
||||
* @property Lang $lang
|
||||
* @property Middleware $middleware
|
||||
* @property Request $request
|
||||
* @property Response $response
|
||||
* @property Route $route
|
||||
* @property Session $session
|
||||
* @property Template $template
|
||||
* @property Url $url
|
||||
* @property Validate $validate
|
||||
* @property View $view
|
||||
* @property route\RuleName $rule_name
|
||||
* @property Log $log
|
||||
*/
|
||||
class Container implements ArrayAccess, IteratorAggregate, Countable
|
||||
{
|
||||
/**
|
||||
* 容器对象实例
|
||||
* @var Container
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* 容器中的对象实例
|
||||
* @var array
|
||||
*/
|
||||
protected $instances = [];
|
||||
|
||||
/**
|
||||
* 容器绑定标识
|
||||
* @var array
|
||||
*/
|
||||
protected $bind = [
|
||||
'app' => App::class,
|
||||
'build' => Build::class,
|
||||
'cache' => Cache::class,
|
||||
'config' => Config::class,
|
||||
'cookie' => Cookie::class,
|
||||
'debug' => Debug::class,
|
||||
'env' => Env::class,
|
||||
'hook' => Hook::class,
|
||||
'lang' => Lang::class,
|
||||
'log' => Log::class,
|
||||
'middleware' => Middleware::class,
|
||||
'request' => Request::class,
|
||||
'response' => Response::class,
|
||||
'route' => Route::class,
|
||||
'session' => Session::class,
|
||||
'template' => Template::class,
|
||||
'url' => Url::class,
|
||||
'validate' => Validate::class,
|
||||
'view' => View::class,
|
||||
'rule_name' => route\RuleName::class,
|
||||
// 接口依赖注入
|
||||
'think\LoggerInterface' => Log::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* 容器标识别名
|
||||
* @var array
|
||||
*/
|
||||
protected $name = [];
|
||||
|
||||
/**
|
||||
* 获取当前容器的实例(单例)
|
||||
* @access public
|
||||
* @return static
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(static::$instance)) {
|
||||
static::$instance = new static;
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前容器的实例
|
||||
* @access public
|
||||
* @param object $instance
|
||||
* @return void
|
||||
*/
|
||||
public static function setInstance($instance)
|
||||
{
|
||||
static::$instance = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取容器中的对象实例
|
||||
* @access public
|
||||
* @param string $abstract 类名或者标识
|
||||
* @param array|true $vars 变量
|
||||
* @param bool $newInstance 是否每次创建新的实例
|
||||
* @return object
|
||||
*/
|
||||
public static function get($abstract, $vars = [], $newInstance = false)
|
||||
{
|
||||
return static::getInstance()->make($abstract, $vars, $newInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定一个类、闭包、实例、接口实现到容器
|
||||
* @access public
|
||||
* @param string $abstract 类标识、接口
|
||||
* @param mixed $concrete 要绑定的类、闭包或者实例
|
||||
* @return Container
|
||||
*/
|
||||
public static function set($abstract, $concrete = null)
|
||||
{
|
||||
return static::getInstance()->bindTo($abstract, $concrete);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除容器中的对象实例
|
||||
* @access public
|
||||
* @param string $abstract 类标识、接口
|
||||
* @return void
|
||||
*/
|
||||
public static function remove($abstract)
|
||||
{
|
||||
return static::getInstance()->delete($abstract);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除容器中的对象实例
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function clear()
|
||||
{
|
||||
return static::getInstance()->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定一个类、闭包、实例、接口实现到容器
|
||||
* @access public
|
||||
* @param string|array $abstract 类标识、接口
|
||||
* @param mixed $concrete 要绑定的类、闭包或者实例
|
||||
* @return $this
|
||||
*/
|
||||
public function bindTo($abstract, $concrete = null)
|
||||
{
|
||||
if (is_array($abstract)) {
|
||||
$this->bind = array_merge($this->bind, $abstract);
|
||||
} elseif ($concrete instanceof Closure) {
|
||||
$this->bind[$abstract] = $concrete;
|
||||
} elseif (is_object($concrete)) {
|
||||
if (isset($this->bind[$abstract])) {
|
||||
$abstract = $this->bind[$abstract];
|
||||
}
|
||||
$this->instances[$abstract] = $concrete;
|
||||
} else {
|
||||
$this->bind[$abstract] = $concrete;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定一个类实例当容器
|
||||
* @access public
|
||||
* @param string $abstract 类名或者标识
|
||||
* @param object|\Closure $instance 类的实例
|
||||
* @return $this
|
||||
*/
|
||||
public function instance($abstract, $instance)
|
||||
{
|
||||
if ($instance instanceof \Closure) {
|
||||
$this->bind[$abstract] = $instance;
|
||||
} else {
|
||||
if (isset($this->bind[$abstract])) {
|
||||
$abstract = $this->bind[$abstract];
|
||||
}
|
||||
|
||||
$this->instances[$abstract] = $instance;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断容器中是否存在类及标识
|
||||
* @access public
|
||||
* @param string $abstract 类名或者标识
|
||||
* @return bool
|
||||
*/
|
||||
public function bound($abstract)
|
||||
{
|
||||
return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断容器中是否存在对象实例
|
||||
* @access public
|
||||
* @param string $abstract 类名或者标识
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($abstract)
|
||||
{
|
||||
if (isset($this->bind[$abstract])) {
|
||||
$abstract = $this->bind[$abstract];
|
||||
}
|
||||
|
||||
return isset($this->instances[$abstract]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断容器中是否存在类及标识
|
||||
* @access public
|
||||
* @param string $name 类名或者标识
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return $this->bound($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建类的实例
|
||||
* @access public
|
||||
* @param string $abstract 类名或者标识
|
||||
* @param array|true $vars 变量
|
||||
* @param bool $newInstance 是否每次创建新的实例
|
||||
* @return object
|
||||
*/
|
||||
public function make($abstract, $vars = [], $newInstance = false)
|
||||
{
|
||||
if (true === $vars) {
|
||||
// 总是创建新的实例化对象
|
||||
$newInstance = true;
|
||||
$vars = [];
|
||||
}
|
||||
|
||||
$abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
|
||||
|
||||
if (isset($this->instances[$abstract]) && !$newInstance) {
|
||||
return $this->instances[$abstract];
|
||||
}
|
||||
|
||||
if (isset($this->bind[$abstract])) {
|
||||
$concrete = $this->bind[$abstract];
|
||||
|
||||
if ($concrete instanceof Closure) {
|
||||
$object = $this->invokeFunction($concrete, $vars);
|
||||
} else {
|
||||
$this->name[$abstract] = $concrete;
|
||||
return $this->make($concrete, $vars, $newInstance);
|
||||
}
|
||||
} else {
|
||||
$object = $this->invokeClass($abstract, $vars);
|
||||
}
|
||||
|
||||
if (!$newInstance) {
|
||||
$this->instances[$abstract] = $object;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除容器中的对象实例
|
||||
* @access public
|
||||
* @param string|array $abstract 类名或者标识
|
||||
* @return void
|
||||
*/
|
||||
public function delete($abstract)
|
||||
{
|
||||
foreach ((array) $abstract as $name) {
|
||||
$name = isset($this->name[$name]) ? $this->name[$name] : $name;
|
||||
|
||||
if (isset($this->instances[$name])) {
|
||||
unset($this->instances[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取容器中的对象实例
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $this->instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除容器中的对象实例
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$this->instances = [];
|
||||
$this->bind = [];
|
||||
$this->name = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行函数或者闭包方法 支持参数调用
|
||||
* @access public
|
||||
* @param mixed $function 函数或者闭包
|
||||
* @param array $vars 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function invokeFunction($function, $vars = [])
|
||||
{
|
||||
try {
|
||||
$reflect = new ReflectionFunction($function);
|
||||
|
||||
$args = $this->bindParams($reflect, $vars);
|
||||
|
||||
return call_user_func_array($function, $args);
|
||||
} catch (ReflectionException $e) {
|
||||
throw new Exception('function not exists: ' . $function . '()');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用反射执行类的方法 支持参数绑定
|
||||
* @access public
|
||||
* @param mixed $method 方法
|
||||
* @param array $vars 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function invokeMethod($method, $vars = [])
|
||||
{
|
||||
try {
|
||||
if (is_array($method)) {
|
||||
$class = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
|
||||
$reflect = new ReflectionMethod($class, $method[1]);
|
||||
} else {
|
||||
// 静态方法
|
||||
$reflect = new ReflectionMethod($method);
|
||||
}
|
||||
|
||||
$args = $this->bindParams($reflect, $vars);
|
||||
|
||||
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
|
||||
} catch (ReflectionException $e) {
|
||||
if (is_array($method) && is_object($method[0])) {
|
||||
$method[0] = get_class($method[0]);
|
||||
}
|
||||
|
||||
throw new Exception('method not exists: ' . (is_array($method) ? $method[0] . '::' . $method[1] : $method) . '()');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用反射执行类的方法 支持参数绑定
|
||||
* @access public
|
||||
* @param object $instance 对象实例
|
||||
* @param mixed $reflect 反射类
|
||||
* @param array $vars 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function invokeReflectMethod($instance, $reflect, $vars = [])
|
||||
{
|
||||
$args = $this->bindParams($reflect, $vars);
|
||||
|
||||
return $reflect->invokeArgs($instance, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用反射执行callable 支持参数绑定
|
||||
* @access public
|
||||
* @param mixed $callable
|
||||
* @param array $vars 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function invoke($callable, $vars = [])
|
||||
{
|
||||
if ($callable instanceof Closure) {
|
||||
return $this->invokeFunction($callable, $vars);
|
||||
}
|
||||
|
||||
return $this->invokeMethod($callable, $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用反射执行类的实例化 支持依赖注入
|
||||
* @access public
|
||||
* @param string $class 类名
|
||||
* @param array $vars 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function invokeClass($class, $vars = [])
|
||||
{
|
||||
try {
|
||||
$reflect = new ReflectionClass($class);
|
||||
|
||||
if ($reflect->hasMethod('__make')) {
|
||||
$method = new ReflectionMethod($class, '__make');
|
||||
|
||||
if ($method->isPublic() && $method->isStatic()) {
|
||||
$args = $this->bindParams($method, $vars);
|
||||
return $method->invokeArgs(null, $args);
|
||||
}
|
||||
}
|
||||
|
||||
$constructor = $reflect->getConstructor();
|
||||
|
||||
$args = $constructor ? $this->bindParams($constructor, $vars) : [];
|
||||
|
||||
return $reflect->newInstanceArgs($args);
|
||||
|
||||
} catch (ReflectionException $e) {
|
||||
throw new ClassNotFoundException('class not exists: ' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定参数
|
||||
* @access protected
|
||||
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
|
||||
* @param array $vars 参数
|
||||
* @return array
|
||||
*/
|
||||
protected function bindParams($reflect, $vars = [])
|
||||
{
|
||||
if ($reflect->getNumberOfParameters() == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 判断数组类型 数字数组时按顺序绑定参数
|
||||
reset($vars);
|
||||
$type = key($vars) === 0 ? 1 : 0;
|
||||
$params = $reflect->getParameters();
|
||||
|
||||
if (PHP_VERSION > 8.0) {
|
||||
$args = $this->parseParamsForPHP8($params, $vars, $type);
|
||||
} else {
|
||||
$args = $this->parseParams($params, $vars, $type);
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析参数
|
||||
* @access protected
|
||||
* @param array $params 参数列表
|
||||
* @param array $vars 参数数据
|
||||
* @param int $type 参数类别
|
||||
* @return array
|
||||
*/
|
||||
protected function parseParams($params, $vars, $type)
|
||||
{
|
||||
foreach ($params as $param) {
|
||||
$name = $param->getName();
|
||||
$lowerName = Loader::parseName($name);
|
||||
$class = $param->getClass();
|
||||
|
||||
if ($class) {
|
||||
$args[] = $this->getObjectParam($class->getName(), $vars);
|
||||
} elseif (1 == $type && !empty($vars)) {
|
||||
$args[] = array_shift($vars);
|
||||
} elseif (0 == $type && isset($vars[$name])) {
|
||||
$args[] = $vars[$name];
|
||||
} elseif (0 == $type && isset($vars[$lowerName])) {
|
||||
$args[] = $vars[$lowerName];
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$args[] = $param->getDefaultValue();
|
||||
} else {
|
||||
throw new InvalidArgumentException('method param miss:' . $name);
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析参数
|
||||
* @access protected
|
||||
* @param array $params 参数列表
|
||||
* @param array $vars 参数数据
|
||||
* @param int $type 参数类别
|
||||
* @return array
|
||||
*/
|
||||
protected function parseParamsForPHP8($params, $vars, $type)
|
||||
{
|
||||
foreach ($params as $param) {
|
||||
$name = $param->getName();
|
||||
$lowerName = Loader::parseName($name);
|
||||
$reflectionType = $param->getType();
|
||||
|
||||
if ($reflectionType && $reflectionType->isBuiltin() === false) {
|
||||
$args[] = $this->getObjectParam($reflectionType->getName(), $vars);
|
||||
} elseif (1 == $type && !empty($vars)) {
|
||||
$args[] = array_shift($vars);
|
||||
} elseif (0 == $type && array_key_exists($name, $vars)) {
|
||||
$args[] = $vars[$name];
|
||||
} elseif (0 == $type && array_key_exists($lowerName, $vars)) {
|
||||
$args[] = $vars[$lowerName];
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$args[] = $param->getDefaultValue();
|
||||
} else {
|
||||
throw new InvalidArgumentException('method param miss:' . $name);
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象类型的参数值
|
||||
* @access protected
|
||||
* @param string $className 类名
|
||||
* @param array $vars 参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getObjectParam($className, &$vars)
|
||||
{
|
||||
$array = $vars;
|
||||
$value = array_shift($array);
|
||||
|
||||
if ($value instanceof $className) {
|
||||
$result = $value;
|
||||
array_shift($vars);
|
||||
} else {
|
||||
$result = $this->make($className);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->bindTo($name, $value);
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->make($name);
|
||||
}
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
return $this->bound($name);
|
||||
}
|
||||
|
||||
public function __unset($name)
|
||||
{
|
||||
$this->delete($name);
|
||||
}
|
||||
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return $this->__isset($key);
|
||||
}
|
||||
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return $this->__get($key);
|
||||
}
|
||||
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
$this->__set($key, $value);
|
||||
}
|
||||
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
$this->__unset($key);
|
||||
}
|
||||
|
||||
//Countable
|
||||
public function count()
|
||||
{
|
||||
return count($this->instances);
|
||||
}
|
||||
|
||||
//IteratorAggregate
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->instances);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['instances'], $data['instance']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
287
Server/thinkphp/library/think/Controller.php
Normal file
287
Server/thinkphp/library/think/Controller.php
Normal file
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\exception\ValidateException;
|
||||
use traits\controller\Jump;
|
||||
|
||||
class Controller
|
||||
{
|
||||
use Jump;
|
||||
|
||||
/**
|
||||
* 视图类实例
|
||||
* @var \think\View
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* Request实例
|
||||
* @var \think\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 验证失败是否抛出异常
|
||||
* @var bool
|
||||
*/
|
||||
protected $failException = false;
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 前置操作方法列表(即将废弃)
|
||||
* @var array $beforeActionList
|
||||
*/
|
||||
protected $beforeActionList = [];
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
*/
|
||||
public function __construct(App $app = null)
|
||||
{
|
||||
$this->app = $app ?: Container::get('app');
|
||||
$this->request = $this->app['request'];
|
||||
$this->view = $this->app['view'];
|
||||
|
||||
// 控制器初始化
|
||||
$this->initialize();
|
||||
|
||||
$this->registerMiddleware();
|
||||
|
||||
// 前置操作方法 即将废弃
|
||||
foreach ((array) $this->beforeActionList as $method => $options) {
|
||||
is_numeric($method) ?
|
||||
$this->beforeAction($options) :
|
||||
$this->beforeAction($method, $options);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
protected function initialize()
|
||||
{}
|
||||
|
||||
// 注册控制器中间件
|
||||
public function registerMiddleware()
|
||||
{
|
||||
foreach ($this->middleware as $key => $val) {
|
||||
if (!is_int($key)) {
|
||||
$only = $except = null;
|
||||
|
||||
if (isset($val['only'])) {
|
||||
$only = array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, $val['only']);
|
||||
} elseif (isset($val['except'])) {
|
||||
$except = array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, $val['except']);
|
||||
}
|
||||
|
||||
if (isset($only) && !in_array($this->request->action(), $only)) {
|
||||
continue;
|
||||
} elseif (isset($except) && in_array($this->request->action(), $except)) {
|
||||
continue;
|
||||
} else {
|
||||
$val = $key;
|
||||
}
|
||||
}
|
||||
|
||||
$this->app['middleware']->controller($val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置操作
|
||||
* @access protected
|
||||
* @param string $method 前置操作方法名
|
||||
* @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]]
|
||||
*/
|
||||
protected function beforeAction($method, $options = [])
|
||||
{
|
||||
if (isset($options['only'])) {
|
||||
if (is_string($options['only'])) {
|
||||
$options['only'] = explode(',', $options['only']);
|
||||
}
|
||||
|
||||
$only = array_map(function ($val) {
|
||||
return strtolower($val);
|
||||
}, $options['only']);
|
||||
|
||||
if (!in_array($this->request->action(), $only)) {
|
||||
return;
|
||||
}
|
||||
} elseif (isset($options['except'])) {
|
||||
if (is_string($options['except'])) {
|
||||
$options['except'] = explode(',', $options['except']);
|
||||
}
|
||||
|
||||
$except = array_map(function ($val) {
|
||||
return strtolower($val);
|
||||
}, $options['except']);
|
||||
|
||||
if (in_array($this->request->action(), $except)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
call_user_func([$this, $method]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载模板输出
|
||||
* @access protected
|
||||
* @param string $template 模板文件名
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetch($template = '', $vars = [], $config = [])
|
||||
{
|
||||
return Response::create($template, 'view')->assign($vars)->config($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容输出
|
||||
* @access protected
|
||||
* @param string $content 模板内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function display($content = '', $vars = [], $config = [])
|
||||
{
|
||||
return Response::create($content, 'view')->assign($vars)->config($config)->isContent(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access protected
|
||||
* @param mixed $name 要显示的模板变量
|
||||
* @param mixed $value 变量的值
|
||||
* @return $this
|
||||
*/
|
||||
protected function assign($name, $value = '')
|
||||
{
|
||||
$this->view->assign($name, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图过滤
|
||||
* @access protected
|
||||
* @param Callable $filter 过滤方法或闭包
|
||||
* @return $this
|
||||
*/
|
||||
protected function filter($filter)
|
||||
{
|
||||
$this->view->filter($filter);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化模板引擎
|
||||
* @access protected
|
||||
* @param array|string $engine 引擎参数
|
||||
* @return $this
|
||||
*/
|
||||
protected function engine($engine)
|
||||
{
|
||||
$this->view->engine($engine);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置验证失败后是否抛出异常
|
||||
* @access protected
|
||||
* @param bool $fail 是否抛出异常
|
||||
* @return $this
|
||||
*/
|
||||
protected function validateFailException($fail = true)
|
||||
{
|
||||
$this->failException = $fail;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @param mixed $callback 回调方法(闭包)
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate($data, $validate, $message = [], $batch = false, $callback = null)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = $this->app->validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
list($validate, $scene) = explode('.', $validate);
|
||||
}
|
||||
$v = $this->app->validate($validate);
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
if (is_array($message)) {
|
||||
$v->message($message);
|
||||
}
|
||||
|
||||
if ($callback && is_callable($callback)) {
|
||||
call_user_func_array($callback, [$v, &$data]);
|
||||
}
|
||||
|
||||
if (!$v->check($data)) {
|
||||
if ($this->failException) {
|
||||
throw new ValidateException($v->getError());
|
||||
}
|
||||
return $v->getError();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app'], $data['request']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
268
Server/thinkphp/library/think/Cookie.php
Normal file
268
Server/thinkphp/library/think/Cookie.php
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Cookie
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [
|
||||
// cookie 名称前缀
|
||||
'prefix' => '',
|
||||
// cookie 保存时间
|
||||
'expire' => 0,
|
||||
// cookie 保存路径
|
||||
'path' => '/',
|
||||
// cookie 有效域名
|
||||
'domain' => '',
|
||||
// cookie 启用安全传输
|
||||
'secure' => false,
|
||||
// httponly设置
|
||||
'httponly' => false,
|
||||
// 是否使用 setcookie
|
||||
'setcookie' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->init($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie初始化
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->config = array_merge($this->config, array_change_key_case($config));
|
||||
|
||||
if (!empty($this->config['httponly']) && PHP_SESSION_ACTIVE != session_status()) {
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static function __make(Config $config)
|
||||
{
|
||||
return new static($config->pull('cookie'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置或者获取cookie作用域(前缀)
|
||||
* @access public
|
||||
* @param string $prefix
|
||||
* @return string|void
|
||||
*/
|
||||
public function prefix($prefix = '')
|
||||
{
|
||||
if (empty($prefix)) {
|
||||
return $this->config['prefix'];
|
||||
}
|
||||
|
||||
$this->config['prefix'] = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie 设置、获取、删除
|
||||
*
|
||||
* @access public
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param mixed $option 可选参数 可能会是 null|integer|string
|
||||
* @return void
|
||||
*/
|
||||
public function set($name, $value = '', $option = null)
|
||||
{
|
||||
// 参数设置(会覆盖黙认设置)
|
||||
if (!is_null($option)) {
|
||||
if (is_numeric($option)) {
|
||||
$option = ['expire' => $option];
|
||||
} elseif (is_string($option)) {
|
||||
parse_str($option, $option);
|
||||
}
|
||||
|
||||
$config = array_merge($this->config, array_change_key_case($option));
|
||||
} else {
|
||||
$config = $this->config;
|
||||
}
|
||||
|
||||
$name = $config['prefix'] . $name;
|
||||
|
||||
// 设置cookie
|
||||
if (is_array($value)) {
|
||||
array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'encode');
|
||||
$value = 'think:' . json_encode($value);
|
||||
}
|
||||
|
||||
$expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0;
|
||||
|
||||
if ($config['setcookie']) {
|
||||
$this->setCookie($name, $value, $expire, $config);
|
||||
}
|
||||
|
||||
$_COOKIE[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie 设置保存
|
||||
*
|
||||
* @access public
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param array $option 可选参数
|
||||
* @return void
|
||||
*/
|
||||
protected function setCookie($name, $value, $expire, $option = [])
|
||||
{
|
||||
setcookie($name, $value, $expire, $option['path'], $option['domain'], $option['secure'], $option['httponly']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 永久保存Cookie数据
|
||||
* @access public
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param mixed $option 可选参数 可能会是 null|integer|string
|
||||
* @return void
|
||||
*/
|
||||
public function forever($name, $value = '', $option = null)
|
||||
{
|
||||
if (is_null($option) || is_numeric($option)) {
|
||||
$option = [];
|
||||
}
|
||||
|
||||
$option['expire'] = 315360000;
|
||||
|
||||
$this->set($name, $value, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Cookie数据
|
||||
* @access public
|
||||
* @param string $name cookie名称
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name, $prefix = null)
|
||||
{
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->config['prefix'];
|
||||
$name = $prefix . $name;
|
||||
|
||||
return isset($_COOKIE[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie获取
|
||||
* @access public
|
||||
* @param string $name cookie名称 留空获取全部
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name = '', $prefix = null)
|
||||
{
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->config['prefix'];
|
||||
$key = $prefix . $name;
|
||||
|
||||
if ('' == $name) {
|
||||
if ($prefix) {
|
||||
$value = [];
|
||||
foreach ($_COOKIE as $k => $val) {
|
||||
if (0 === strpos($k, $prefix)) {
|
||||
$value[$k] = $val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$value = $_COOKIE;
|
||||
}
|
||||
} elseif (isset($_COOKIE[$key])) {
|
||||
$value = $_COOKIE[$key];
|
||||
|
||||
if (0 === strpos($value, 'think:')) {
|
||||
$value = substr($value, 6);
|
||||
$value = json_decode($value, true);
|
||||
array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'decode');
|
||||
}
|
||||
} else {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie删除
|
||||
* @access public
|
||||
* @param string $name cookie名称
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return void
|
||||
*/
|
||||
public function delete($name, $prefix = null)
|
||||
{
|
||||
$config = $this->config;
|
||||
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
||||
$name = $prefix . $name;
|
||||
|
||||
if ($config['setcookie']) {
|
||||
$this->setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config);
|
||||
}
|
||||
|
||||
// 删除指定cookie
|
||||
unset($_COOKIE[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie清空
|
||||
* @access public
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return void
|
||||
*/
|
||||
public function clear($prefix = null)
|
||||
{
|
||||
// 清除指定前缀的所有cookie
|
||||
if (empty($_COOKIE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 要删除的cookie前缀,不指定则删除config设置的指定前缀
|
||||
$config = $this->config;
|
||||
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
||||
|
||||
if ($prefix) {
|
||||
// 如果前缀为空字符串将不作处理直接返回
|
||||
foreach ($_COOKIE as $key => $val) {
|
||||
if (0 === strpos($key, $prefix)) {
|
||||
if ($config['setcookie']) {
|
||||
$this->setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config);
|
||||
}
|
||||
unset($_COOKIE[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private function jsonFormatProtect(&$val, $key, $type = 'encode')
|
||||
{
|
||||
if (!empty($val) && true !== $val) {
|
||||
$val = 'decode' == $type ? urldecode($val) : urlencode($val);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
197
Server/thinkphp/library/think/Db.php
Normal file
197
Server/thinkphp/library/think/Db.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\db\Connection;
|
||||
|
||||
/**
|
||||
* Class Db
|
||||
* @package think
|
||||
* @method \think\db\Query master() static 从主服务器读取数据
|
||||
* @method \think\db\Query readMaster(bool $all = false) static 后续从主服务器读取数据
|
||||
* @method \think\db\Query table(string $table) static 指定数据表(含前缀)
|
||||
* @method \think\db\Query name(string $name) static 指定数据表(不含前缀)
|
||||
* @method \think\db\Expression raw(string $value) static 使用表达式设置数据
|
||||
* @method \think\db\Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
|
||||
* @method \think\db\Query whereRaw(string $where, array $bind = []) static 表达式查询
|
||||
* @method \think\db\Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询
|
||||
* @method \think\db\Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
|
||||
* @method \think\db\Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
|
||||
* @method \think\db\Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
|
||||
* @method \think\db\Query field(mixed $field, boolean $except = false) static 指定查询字段
|
||||
* @method \think\db\Query fieldRaw(string $field, array $bind = []) static 指定查询字段
|
||||
* @method \think\db\Query union(mixed $union, boolean $all = false) static UNION查询
|
||||
* @method \think\db\Query limit(mixed $offset, integer $length = null) static 查询LIMIT
|
||||
* @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER
|
||||
* @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER
|
||||
* @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
|
||||
* @method \think\db\Query withAttr(string $name,callable $callback = null) static 使用获取器获取数据
|
||||
* @method mixed value(string $field) static 获取某个字段的值
|
||||
* @method array column(string $field, string $key = '') static 获取某个列的值
|
||||
* @method mixed find(mixed $data = null) static 查询单个记录
|
||||
* @method mixed select(mixed $data = null) static 查询多个记录
|
||||
* @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录
|
||||
* @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID
|
||||
* @method integer insertAll(array $dataSet) static 插入多条记录
|
||||
* @method integer update(array $data) static 更新记录
|
||||
* @method integer delete(mixed $data = null) static 删除记录
|
||||
* @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
|
||||
* @method \Generator cursor(mixed $data = null) static 使用游标查找记录
|
||||
* @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
|
||||
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
|
||||
* @method \think\Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
|
||||
* @method mixed transaction(callable $callback) static 执行数据库事务
|
||||
* @method void startTrans() static 启动事务
|
||||
* @method void commit() static 用于非自动提交状态下面的查询提交
|
||||
* @method void rollback() static 事务回滚
|
||||
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
|
||||
* @method string getLastInsID(string $sequence = null) static 获取最近插入的ID
|
||||
*/
|
||||
class Db
|
||||
{
|
||||
/**
|
||||
* 当前数据库连接对象
|
||||
* @var Connection
|
||||
*/
|
||||
protected static $connection;
|
||||
|
||||
/**
|
||||
* 数据库配置
|
||||
* @var array
|
||||
*/
|
||||
protected static $config = [];
|
||||
|
||||
/**
|
||||
* 查询次数
|
||||
* @var integer
|
||||
*/
|
||||
public static $queryTimes = 0;
|
||||
|
||||
/**
|
||||
* 执行次数
|
||||
* @var integer
|
||||
*/
|
||||
public static $executeTimes = 0;
|
||||
|
||||
/**
|
||||
* 配置
|
||||
* @access public
|
||||
* @param mixed $config
|
||||
* @return void
|
||||
*/
|
||||
public static function init($config = [])
|
||||
{
|
||||
self::$config = $config;
|
||||
|
||||
if (empty($config['query'])) {
|
||||
self::$config['query'] = '\\think\\db\\Query';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库配置
|
||||
* @access public
|
||||
* @param string $config 配置名称
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getConfig($name = '')
|
||||
{
|
||||
if ('' === $name) {
|
||||
return self::$config;
|
||||
}
|
||||
|
||||
return isset(self::$config[$name]) ? self::$config[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换数据库连接
|
||||
* @access public
|
||||
* @param mixed $config 连接配置
|
||||
* @param bool|string $name 连接标识 true 强制重新连接
|
||||
* @param string $query 查询对象类名
|
||||
* @return mixed 返回查询对象实例
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function connect($config = [], $name = false, $query = '')
|
||||
{
|
||||
// 解析配置参数
|
||||
$options = self::parseConfig($config ?: self::$config);
|
||||
|
||||
$query = $query ?: $options['query'];
|
||||
|
||||
// 创建数据库连接对象实例
|
||||
self::$connection = Connection::instance($options, $name);
|
||||
|
||||
return new $query(self::$connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库连接参数解析
|
||||
* @access private
|
||||
* @param mixed $config
|
||||
* @return array
|
||||
*/
|
||||
private static function parseConfig($config)
|
||||
{
|
||||
if (is_string($config) && false === strpos($config, '/')) {
|
||||
// 支持读取配置参数
|
||||
$config = isset(self::$config[$config]) ? self::$config[$config] : self::$config;
|
||||
}
|
||||
|
||||
$result = is_string($config) ? self::parseDsnConfig($config) : $config;
|
||||
|
||||
if (empty($result['query'])) {
|
||||
$result['query'] = self::$config['query'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* DSN解析
|
||||
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8
|
||||
* @access private
|
||||
* @param string $dsnStr
|
||||
* @return array
|
||||
*/
|
||||
private static function parseDsnConfig($dsnStr)
|
||||
{
|
||||
$info = parse_url($dsnStr);
|
||||
|
||||
if (!$info) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dsn = [
|
||||
'type' => $info['scheme'],
|
||||
'username' => isset($info['user']) ? $info['user'] : '',
|
||||
'password' => isset($info['pass']) ? $info['pass'] : '',
|
||||
'hostname' => isset($info['host']) ? $info['host'] : '',
|
||||
'hostport' => isset($info['port']) ? $info['port'] : '',
|
||||
'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '',
|
||||
'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8',
|
||||
];
|
||||
|
||||
if (isset($info['query'])) {
|
||||
parse_str($info['query'], $dsn['params']);
|
||||
} else {
|
||||
$dsn['params'] = [];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
return call_user_func_array([static::connect(), $method], $args);
|
||||
}
|
||||
}
|
||||
278
Server/thinkphp/library/think/Debug.php
Normal file
278
Server/thinkphp/library/think/Debug.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\model\Collection as ModelCollection;
|
||||
use think\response\Redirect;
|
||||
|
||||
class Debug
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 区间时间信息
|
||||
* @var array
|
||||
*/
|
||||
protected $info = [];
|
||||
|
||||
/**
|
||||
* 区间内存信息
|
||||
* @var array
|
||||
*/
|
||||
protected $mem = [];
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app, array $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
return new static($app, $config->pull('trace'));
|
||||
}
|
||||
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录时间(微秒)和内存使用情况
|
||||
* @access public
|
||||
* @param string $name 标记位置
|
||||
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存
|
||||
* @return void
|
||||
*/
|
||||
public function remark($name, $value = '')
|
||||
{
|
||||
// 记录时间和内存使用
|
||||
$this->info[$name] = is_float($value) ? $value : microtime(true);
|
||||
|
||||
if ('time' != $value) {
|
||||
$this->mem['mem'][$name] = is_float($value) ? $value : memory_get_usage();
|
||||
$this->mem['peak'][$name] = memory_get_peak_usage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计某个区间的时间(微秒)使用情况
|
||||
* @access public
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return integer
|
||||
*/
|
||||
public function getRangeTime($start, $end, $dec = 6)
|
||||
{
|
||||
if (!isset($this->info[$end])) {
|
||||
$this->info[$end] = microtime(true);
|
||||
}
|
||||
|
||||
return number_format(($this->info[$end] - $this->info[$start]), $dec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计从开始到统计时的时间(微秒)使用情况
|
||||
* @access public
|
||||
* @param integer|string $dec 小数位
|
||||
* @return integer
|
||||
*/
|
||||
public function getUseTime($dec = 6)
|
||||
{
|
||||
return number_format((microtime(true) - $this->app->getBeginTime()), $dec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前访问的吞吐率情况
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getThroughputRate()
|
||||
{
|
||||
return number_format(1 / $this->getUseTime(), 2) . 'req/s';
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录区间的内存使用情况
|
||||
* @access public
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public function getRangeMem($start, $end, $dec = 2)
|
||||
{
|
||||
if (!isset($this->mem['mem'][$end])) {
|
||||
$this->mem['mem'][$end] = memory_get_usage();
|
||||
}
|
||||
|
||||
$size = $this->mem['mem'][$end] - $this->mem['mem'][$start];
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
|
||||
while ($size >= 1024) {
|
||||
$size /= 1024;
|
||||
$pos++;
|
||||
}
|
||||
|
||||
return round($size, $dec) . " " . $a[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计从开始到统计时的内存使用情况
|
||||
* @access public
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public function getUseMem($dec = 2)
|
||||
{
|
||||
$size = memory_get_usage() - $this->app->getBeginMem();
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
|
||||
while ($size >= 1024) {
|
||||
$size /= 1024;
|
||||
$pos++;
|
||||
}
|
||||
|
||||
return round($size, $dec) . " " . $a[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计区间的内存峰值情况
|
||||
* @access public
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public function getMemPeak($start, $end, $dec = 2)
|
||||
{
|
||||
if (!isset($this->mem['peak'][$end])) {
|
||||
$this->mem['peak'][$end] = memory_get_peak_usage();
|
||||
}
|
||||
|
||||
$size = $this->mem['peak'][$end] - $this->mem['peak'][$start];
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
|
||||
while ($size >= 1024) {
|
||||
$size /= 1024;
|
||||
$pos++;
|
||||
}
|
||||
|
||||
return round($size, $dec) . " " . $a[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件加载信息
|
||||
* @access public
|
||||
* @param bool $detail 是否显示详细
|
||||
* @return integer|array
|
||||
*/
|
||||
public function getFile($detail = false)
|
||||
{
|
||||
if ($detail) {
|
||||
$files = get_included_files();
|
||||
$info = [];
|
||||
|
||||
foreach ($files as $key => $file) {
|
||||
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
return count(get_included_files());
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览器友好的变量输出
|
||||
* @access public
|
||||
* @param mixed $var 变量
|
||||
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
|
||||
* @param string $label 标签 默认为空
|
||||
* @param integer $flags htmlspecialchars flags
|
||||
* @return void|string
|
||||
*/
|
||||
public function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
|
||||
{
|
||||
$label = (null === $label) ? '' : rtrim($label) . ':';
|
||||
if ($var instanceof Model || $var instanceof ModelCollection) {
|
||||
$var = $var->toArray();
|
||||
}
|
||||
|
||||
ob_start();
|
||||
var_dump($var);
|
||||
|
||||
$output = ob_get_clean();
|
||||
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
$output = PHP_EOL . $label . $output . PHP_EOL;
|
||||
} else {
|
||||
if (!extension_loaded('xdebug')) {
|
||||
$output = htmlspecialchars($output, $flags);
|
||||
}
|
||||
$output = '<pre>' . $label . $output . '</pre>';
|
||||
}
|
||||
if ($echo) {
|
||||
echo($output);
|
||||
return;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function inject(Response $response, &$content)
|
||||
{
|
||||
$config = $this->config;
|
||||
$type = isset($config['type']) ? $config['type'] : 'Html';
|
||||
|
||||
unset($config['type']);
|
||||
|
||||
$trace = Loader::factory($type, '\\think\\debug\\', $config);
|
||||
|
||||
if ($response instanceof Redirect) {
|
||||
//TODO 记录
|
||||
} else {
|
||||
$output = $trace->output($response, $this->app['log']->getLog());
|
||||
if (is_string($output)) {
|
||||
// trace调试信息注入
|
||||
$pos = strripos($content, '</body>');
|
||||
if (false !== $pos) {
|
||||
$content = substr($content, 0, $pos) . $output . substr($content, $pos);
|
||||
} else {
|
||||
$content = $content . $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
113
Server/thinkphp/library/think/Env.php
Normal file
113
Server/thinkphp/library/think/Env.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Env
|
||||
{
|
||||
/**
|
||||
* 环境变量数据
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->data = $_ENV;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取环境变量定义文件
|
||||
* @access public
|
||||
* @param string $file 环境变量定义文件
|
||||
* @return void
|
||||
*/
|
||||
public function load($file)
|
||||
{
|
||||
$env = parse_ini_file($file, true);
|
||||
$this->set($env);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境变量值
|
||||
* @access public
|
||||
* @param string $name 环境变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name = null, $default = null, $php_prefix = true)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
$name = strtoupper(str_replace('.', '_', $name));
|
||||
|
||||
if (isset($this->data[$name])) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
return $this->getEnv($name, $default, $php_prefix);
|
||||
}
|
||||
|
||||
protected function getEnv($name, $default = null, $php_prefix = true)
|
||||
{
|
||||
if ($php_prefix) {
|
||||
$name = 'PHP_' . $name;
|
||||
}
|
||||
|
||||
$result = getenv($name);
|
||||
|
||||
if (false === $result) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ('false' === $result) {
|
||||
$result = false;
|
||||
} elseif ('true' === $result) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
if (!isset($this->data[$name])) {
|
||||
$this->data[$name] = $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置环境变量值
|
||||
* @access public
|
||||
* @param string|array $env 环境变量
|
||||
* @param mixed $value 值
|
||||
* @return void
|
||||
*/
|
||||
public function set($env, $value = null)
|
||||
{
|
||||
if (is_array($env)) {
|
||||
$env = array_change_key_case($env, CASE_UPPER);
|
||||
|
||||
foreach ($env as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
foreach ($val as $k => $v) {
|
||||
$this->data[$key . '_' . strtoupper($k)] = $v;
|
||||
}
|
||||
} else {
|
||||
$this->data[$key] = $val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$name = strtoupper(str_replace('.', '_', $env));
|
||||
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
147
Server/thinkphp/library/think/Error.php
Normal file
147
Server/thinkphp/library/think/Error.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\console\Output as ConsoleOutput;
|
||||
use think\exception\ErrorException;
|
||||
use think\exception\Handle;
|
||||
use think\exception\ThrowableError;
|
||||
|
||||
class Error
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected static $exceptionHandler;
|
||||
|
||||
/**
|
||||
* 注册异常处理
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
error_reporting(E_ALL);
|
||||
set_error_handler([__CLASS__, 'appError']);
|
||||
set_exception_handler([__CLASS__, 'appException']);
|
||||
register_shutdown_function([__CLASS__, 'appShutdown']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception Handler
|
||||
* @access public
|
||||
* @param \Exception|\Throwable $e
|
||||
*/
|
||||
public static function appException($e)
|
||||
{
|
||||
if (!$e instanceof \Exception) {
|
||||
$e = new ThrowableError($e);
|
||||
}
|
||||
|
||||
self::getExceptionHandler()->report($e);
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
|
||||
} else {
|
||||
self::getExceptionHandler()->render($e)->send();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error Handler
|
||||
* @access public
|
||||
* @param integer $errno 错误编号
|
||||
* @param integer $errstr 详细错误信息
|
||||
* @param string $errfile 出错的文件
|
||||
* @param integer $errline 出错行号
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public static function appError($errno, $errstr, $errfile = '', $errline = 0)
|
||||
{
|
||||
$exception = new ErrorException($errno, $errstr, $errfile, $errline);
|
||||
if (error_reporting() & $errno) {
|
||||
// 将错误信息托管至 think\exception\ErrorException
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
self::getExceptionHandler()->report($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown Handler
|
||||
* @access public
|
||||
*/
|
||||
public static function appShutdown()
|
||||
{
|
||||
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
|
||||
// 将错误信息托管至think\ErrorException
|
||||
$exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);
|
||||
|
||||
self::appException($exception);
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
Container::get('log')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定错误类型是否致命
|
||||
*
|
||||
* @access protected
|
||||
* @param int $type
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isFatal($type)
|
||||
{
|
||||
return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置异常处理类
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $handle
|
||||
* @return void
|
||||
*/
|
||||
public static function setExceptionHandler($handle)
|
||||
{
|
||||
self::$exceptionHandler = $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the exception handler.
|
||||
*
|
||||
* @access public
|
||||
* @return Handle
|
||||
*/
|
||||
public static function getExceptionHandler()
|
||||
{
|
||||
static $handle;
|
||||
|
||||
if (!$handle) {
|
||||
// 异常处理handle
|
||||
$class = self::$exceptionHandler;
|
||||
|
||||
if ($class && is_string($class) && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) {
|
||||
$handle = new $class;
|
||||
} else {
|
||||
$handle = new Handle;
|
||||
if ($class instanceof \Closure) {
|
||||
$handle->setRender($class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $handle;
|
||||
}
|
||||
}
|
||||
56
Server/thinkphp/library/think/Exception.php
Normal file
56
Server/thinkphp/library/think/Exception.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* 保存异常页面显示的额外Debug数据
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* 设置异常额外的Debug数据
|
||||
* 数据将会显示为下面的格式
|
||||
*
|
||||
* Exception Data
|
||||
* --------------------------------------------------
|
||||
* Label 1
|
||||
* key1 value1
|
||||
* key2 value2
|
||||
* Label 2
|
||||
* key1 value1
|
||||
* key2 value2
|
||||
*
|
||||
* @access protected
|
||||
* @param string $label 数据分类,用于异常页面显示
|
||||
* @param array $data 需要显示的数据,必须为关联数组
|
||||
*/
|
||||
final protected function setData($label, array $data)
|
||||
{
|
||||
$this->data[$label] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常额外Debug数据
|
||||
* 主要用于输出到异常页面便于调试
|
||||
* @access public
|
||||
* @return array 由setData设置的Debug数据
|
||||
*/
|
||||
final public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
}
|
||||
125
Server/thinkphp/library/think/Facade.php
Normal file
125
Server/thinkphp/library/think/Facade.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Facade
|
||||
{
|
||||
/**
|
||||
* 绑定对象
|
||||
* @var array
|
||||
*/
|
||||
protected static $bind = [];
|
||||
|
||||
/**
|
||||
* 始终创建新的对象实例
|
||||
* @var bool
|
||||
*/
|
||||
protected static $alwaysNewInstance;
|
||||
|
||||
/**
|
||||
* 绑定类的静态代理
|
||||
* @static
|
||||
* @access public
|
||||
* @param string|array $name 类标识
|
||||
* @param string $class 类名
|
||||
* @return object
|
||||
*/
|
||||
public static function bind($name, $class = null)
|
||||
{
|
||||
if (__CLASS__ != static::class) {
|
||||
return self::__callStatic('bind', func_get_args());
|
||||
}
|
||||
|
||||
if (is_array($name)) {
|
||||
self::$bind = array_merge(self::$bind, $name);
|
||||
} else {
|
||||
self::$bind[$name] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Facade实例
|
||||
* @static
|
||||
* @access protected
|
||||
* @param string $class 类名或标识
|
||||
* @param array $args 变量
|
||||
* @param bool $newInstance 是否每次创建新的实例
|
||||
* @return object
|
||||
*/
|
||||
protected static function createFacade($class = '', $args = [], $newInstance = false)
|
||||
{
|
||||
$class = $class ?: static::class;
|
||||
|
||||
$facadeClass = static::getFacadeClass();
|
||||
|
||||
if ($facadeClass) {
|
||||
$class = $facadeClass;
|
||||
} elseif (isset(self::$bind[$class])) {
|
||||
$class = self::$bind[$class];
|
||||
}
|
||||
|
||||
if (static::$alwaysNewInstance) {
|
||||
$newInstance = true;
|
||||
}
|
||||
|
||||
return Container::getInstance()->make($class, $args, $newInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前Facade对应类名(或者已经绑定的容器对象标识)
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected static function getFacadeClass()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 带参数实例化当前Facade类
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public static function instance(...$args)
|
||||
{
|
||||
if (__CLASS__ != static::class) {
|
||||
return self::createFacade('', $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用类的实例
|
||||
* @access public
|
||||
* @param string $class 类名或者标识
|
||||
* @param array|true $args 变量
|
||||
* @param bool $newInstance 是否每次创建新的实例
|
||||
* @return mixed
|
||||
*/
|
||||
public static function make($class, $args = [], $newInstance = false)
|
||||
{
|
||||
if (__CLASS__ != static::class) {
|
||||
return self::__callStatic('make', func_get_args());
|
||||
}
|
||||
|
||||
if (true === $args) {
|
||||
// 总是创建新的实例化对象
|
||||
$newInstance = true;
|
||||
$args = [];
|
||||
}
|
||||
|
||||
return self::createFacade($class, $args, $newInstance);
|
||||
}
|
||||
|
||||
// 调用实际类的方法
|
||||
public static function __callStatic($method, $params)
|
||||
{
|
||||
return call_user_func_array([static::createFacade(), $method], $params);
|
||||
}
|
||||
}
|
||||
496
Server/thinkphp/library/think/File.php
Normal file
496
Server/thinkphp/library/think/File.php
Normal file
@@ -0,0 +1,496 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use SplFileObject;
|
||||
|
||||
class File extends SplFileObject
|
||||
{
|
||||
/**
|
||||
* 错误信息
|
||||
* @var string
|
||||
*/
|
||||
private $error = '';
|
||||
|
||||
/**
|
||||
* 当前完整文件名
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* 上传文件名
|
||||
* @var string
|
||||
*/
|
||||
protected $saveName;
|
||||
|
||||
/**
|
||||
* 上传文件命名规则
|
||||
* @var string
|
||||
*/
|
||||
protected $rule = 'date';
|
||||
|
||||
/**
|
||||
* 上传文件验证规则
|
||||
* @var array
|
||||
*/
|
||||
protected $validate = [];
|
||||
|
||||
/**
|
||||
* 是否单元测试
|
||||
* @var bool
|
||||
*/
|
||||
protected $isTest;
|
||||
|
||||
/**
|
||||
* 上传文件信息
|
||||
* @var array
|
||||
*/
|
||||
protected $info = [];
|
||||
|
||||
/**
|
||||
* 文件hash规则
|
||||
* @var array
|
||||
*/
|
||||
protected $hash = [];
|
||||
|
||||
public function __construct($filename, $mode = 'r')
|
||||
{
|
||||
parent::__construct($filename, $mode);
|
||||
|
||||
$this->filename = $this->getRealPath() ?: $this->getPathname();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否测试
|
||||
* @access public
|
||||
* @param bool $test 是否测试
|
||||
* @return $this
|
||||
*/
|
||||
public function isTest($test = false)
|
||||
{
|
||||
$this->isTest = $test;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传信息
|
||||
* @access public
|
||||
* @param array $info 上传文件信息
|
||||
* @return $this
|
||||
*/
|
||||
public function setUploadInfo($info)
|
||||
{
|
||||
$this->info = $info;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件的信息
|
||||
* @access public
|
||||
* @param string $name
|
||||
* @return array|string
|
||||
*/
|
||||
public function getInfo($name = '')
|
||||
{
|
||||
return isset($this->info[$name]) ? $this->info[$name] : $this->info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件的文件名
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSaveName()
|
||||
{
|
||||
return $this->saveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传文件的保存文件名
|
||||
* @access public
|
||||
* @param string $saveName
|
||||
* @return $this
|
||||
*/
|
||||
public function setSaveName($saveName)
|
||||
{
|
||||
$this->saveName = $saveName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的哈希散列值
|
||||
* @access public
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public function hash($type = 'sha1')
|
||||
{
|
||||
if (!isset($this->hash[$type])) {
|
||||
$this->hash[$type] = hash_file($type, $this->filename);
|
||||
}
|
||||
|
||||
return $this->hash[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查目录是否可写
|
||||
* @access protected
|
||||
* @param string $path 目录
|
||||
* @return boolean
|
||||
*/
|
||||
protected function checkPath($path)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mkdir($path, 0755, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->error = ['directory {:path} creation failed', ['path' => $path]];
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型信息
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getMime()
|
||||
{
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
|
||||
return finfo_file($finfo, $this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文件的命名规则
|
||||
* @access public
|
||||
* @param string $rule 文件命名规则
|
||||
* @return $this
|
||||
*/
|
||||
public function rule($rule)
|
||||
{
|
||||
$this->rule = $rule;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传文件的验证规则
|
||||
* @access public
|
||||
* @param array $rule 验证规则
|
||||
* @return $this
|
||||
*/
|
||||
public function validate($rule = [])
|
||||
{
|
||||
$this->validate = $rule;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否合法的上传文件
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
if ($this->isTest) {
|
||||
return is_file($this->filename);
|
||||
}
|
||||
|
||||
return is_uploaded_file($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件
|
||||
* @access public
|
||||
* @param array $rule 验证规则
|
||||
* @return bool
|
||||
*/
|
||||
public function check($rule = [])
|
||||
{
|
||||
$rule = $rule ?: $this->validate;
|
||||
|
||||
if ((isset($rule['size']) && !$this->checkSize($rule['size']))
|
||||
|| (isset($rule['type']) && !$this->checkMime($rule['type']))
|
||||
|| (isset($rule['ext']) && !$this->checkExt($rule['ext']))
|
||||
|| !$this->checkImg()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件后缀
|
||||
* @access public
|
||||
* @param array|string $ext 允许后缀
|
||||
* @return bool
|
||||
*/
|
||||
public function checkExt($ext)
|
||||
{
|
||||
if (is_string($ext)) {
|
||||
$ext = explode(',', $ext);
|
||||
}
|
||||
|
||||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
||||
|
||||
if (!in_array($extension, $ext)) {
|
||||
$this->error = 'extensions to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测图像文件
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function checkImg()
|
||||
{
|
||||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
||||
|
||||
/* 对图像文件进行严格检测 */
|
||||
if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13])) {
|
||||
$this->error = 'illegal image files';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断图像类型
|
||||
protected function getImageType($image)
|
||||
{
|
||||
if (function_exists('exif_imagetype')) {
|
||||
return exif_imagetype($image);
|
||||
}
|
||||
|
||||
try {
|
||||
$info = getimagesize($image);
|
||||
return $info ? $info[2] : false;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件大小
|
||||
* @access public
|
||||
* @param integer $size 最大大小
|
||||
* @return bool
|
||||
*/
|
||||
public function checkSize($size)
|
||||
{
|
||||
if ($this->getSize() > (int) $size) {
|
||||
$this->error = 'filesize not match';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件类型
|
||||
* @access public
|
||||
* @param array|string $mime 允许类型
|
||||
* @return bool
|
||||
*/
|
||||
public function checkMime($mime)
|
||||
{
|
||||
if (is_string($mime)) {
|
||||
$mime = explode(',', $mime);
|
||||
}
|
||||
|
||||
if (!in_array(strtolower($this->getMime()), $mime)) {
|
||||
$this->error = 'mimetype to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件
|
||||
* @access public
|
||||
* @param string $path 保存路径
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @param boolean $replace 同名文件是否覆盖
|
||||
* @param bool $autoAppendExt 自动补充扩展名
|
||||
* @return false|File false-失败 否则返回File实例
|
||||
*/
|
||||
public function move($path, $savename = true, $replace = true, $autoAppendExt = true)
|
||||
{
|
||||
// 文件上传失败,捕获错误代码
|
||||
if (!empty($this->info['error'])) {
|
||||
$this->error($this->info['error']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检测合法性
|
||||
if (!$this->isValid()) {
|
||||
$this->error = 'upload illegal files';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证上传
|
||||
if (!$this->check()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
// 文件保存命名规则
|
||||
$saveName = $this->buildSaveName($savename, $autoAppendExt);
|
||||
$filename = $path . $saveName;
|
||||
|
||||
// 检测目录
|
||||
if (false === $this->checkPath(dirname($filename))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 不覆盖同名文件 */
|
||||
if (!$replace && is_file($filename)) {
|
||||
$this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 移动文件 */
|
||||
if ($this->isTest) {
|
||||
rename($this->filename, $filename);
|
||||
} elseif (!move_uploaded_file($this->filename, $filename)) {
|
||||
$this->error = 'upload write error';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返回 File对象实例
|
||||
$file = new self($filename);
|
||||
$file->setSaveName($saveName);
|
||||
$file->setUploadInfo($this->info);
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取保存文件名
|
||||
* @access protected
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @param bool $autoAppendExt 自动补充扩展名
|
||||
* @return string
|
||||
*/
|
||||
protected function buildSaveName($savename, $autoAppendExt = true)
|
||||
{
|
||||
if (true === $savename) {
|
||||
// 自动生成文件名
|
||||
$savename = $this->autoBuildName();
|
||||
} elseif ('' === $savename || false === $savename) {
|
||||
// 保留原文件名
|
||||
$savename = $this->getInfo('name');
|
||||
}
|
||||
|
||||
if ($autoAppendExt && false === strpos($savename, '.')) {
|
||||
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
return $savename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成文件名
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function autoBuildName()
|
||||
{
|
||||
if ($this->rule instanceof \Closure) {
|
||||
$savename = call_user_func_array($this->rule, [$this]);
|
||||
} else {
|
||||
switch ($this->rule) {
|
||||
case 'date':
|
||||
$savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
|
||||
break;
|
||||
default:
|
||||
if (in_array($this->rule, hash_algos())) {
|
||||
$hash = $this->hash($this->rule);
|
||||
$savename = substr($hash, 0, 2) . DIRECTORY_SEPARATOR . substr($hash, 2);
|
||||
} elseif (is_callable($this->rule)) {
|
||||
$savename = call_user_func($this->rule);
|
||||
} else {
|
||||
$savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $savename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误代码信息
|
||||
* @access private
|
||||
* @param int $errorNo 错误号
|
||||
*/
|
||||
private function error($errorNo)
|
||||
{
|
||||
switch ($errorNo) {
|
||||
case 1:
|
||||
case 2:
|
||||
$this->error = 'upload File size exceeds the maximum value';
|
||||
break;
|
||||
case 3:
|
||||
$this->error = 'only the portion of file is uploaded';
|
||||
break;
|
||||
case 4:
|
||||
$this->error = 'no file to uploaded';
|
||||
break;
|
||||
case 6:
|
||||
$this->error = 'upload temp dir not found';
|
||||
break;
|
||||
case 7:
|
||||
$this->error = 'file write error';
|
||||
break;
|
||||
default:
|
||||
$this->error = 'unknown upload error';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息(支持多语言)
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
$lang = Container::get('lang');
|
||||
|
||||
if (is_array($this->error)) {
|
||||
list($msg, $vars) = $this->error;
|
||||
} else {
|
||||
$msg = $this->error;
|
||||
$vars = [];
|
||||
}
|
||||
|
||||
return $lang->has($msg) ? $lang->get($msg, $vars) : $msg;
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return $this->hash($method);
|
||||
}
|
||||
}
|
||||
220
Server/thinkphp/library/think/Hook.php
Normal file
220
Server/thinkphp/library/think/Hook.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Hook
|
||||
{
|
||||
/**
|
||||
* 钩子行为定义
|
||||
* @var array
|
||||
*/
|
||||
private $tags = [];
|
||||
|
||||
/**
|
||||
* 绑定行为列表
|
||||
* @var array
|
||||
*/
|
||||
protected $bind = [];
|
||||
|
||||
/**
|
||||
* 入口方法名称
|
||||
* @var string
|
||||
*/
|
||||
private static $portal = 'run';
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定入口方法名称
|
||||
* @access public
|
||||
* @param string $name 方法名
|
||||
* @return $this
|
||||
*/
|
||||
public function portal($name)
|
||||
{
|
||||
self::$portal = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定行为标识 便于调用
|
||||
* @access public
|
||||
* @param string|array $name 行为标识
|
||||
* @param mixed $behavior 行为
|
||||
* @return $this
|
||||
*/
|
||||
public function alias($name, $behavior = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->bind = array_merge($this->bind, $name);
|
||||
} else {
|
||||
$this->bind[$name] = $behavior;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态添加行为扩展到某个标签
|
||||
* @access public
|
||||
* @param string $tag 标签名称
|
||||
* @param mixed $behavior 行为名称
|
||||
* @param bool $first 是否放到开头执行
|
||||
* @return void
|
||||
*/
|
||||
public function add($tag, $behavior, $first = false)
|
||||
{
|
||||
isset($this->tags[$tag]) || $this->tags[$tag] = [];
|
||||
|
||||
if (is_array($behavior) && !is_callable($behavior)) {
|
||||
if (!array_key_exists('_overlay', $behavior)) {
|
||||
$this->tags[$tag] = array_merge($this->tags[$tag], $behavior);
|
||||
} else {
|
||||
unset($behavior['_overlay']);
|
||||
$this->tags[$tag] = $behavior;
|
||||
}
|
||||
} elseif ($first) {
|
||||
array_unshift($this->tags[$tag], $behavior);
|
||||
} else {
|
||||
$this->tags[$tag][] = $behavior;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量导入插件
|
||||
* @access public
|
||||
* @param array $tags 插件信息
|
||||
* @param bool $recursive 是否递归合并
|
||||
* @return void
|
||||
*/
|
||||
public function import(array $tags, $recursive = true)
|
||||
{
|
||||
if ($recursive) {
|
||||
foreach ($tags as $tag => $behavior) {
|
||||
$this->add($tag, $behavior);
|
||||
}
|
||||
} else {
|
||||
$this->tags = $tags + $this->tags;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件信息
|
||||
* @access public
|
||||
* @param string $tag 插件位置 留空获取全部
|
||||
* @return array
|
||||
*/
|
||||
public function get($tag = '')
|
||||
{
|
||||
if (empty($tag)) {
|
||||
//获取全部的插件信息
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
return array_key_exists($tag, $this->tags) ? $this->tags[$tag] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听标签的行为
|
||||
* @access public
|
||||
* @param string $tag 标签名称
|
||||
* @param mixed $params 传入参数
|
||||
* @param bool $once 只获取一个有效返回值
|
||||
* @return mixed
|
||||
*/
|
||||
public function listen($tag, $params = null, $once = false)
|
||||
{
|
||||
$results = [];
|
||||
$tags = $this->get($tag);
|
||||
|
||||
foreach ($tags as $key => $name) {
|
||||
$results[$key] = $this->execTag($name, $tag, $params);
|
||||
|
||||
if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $once ? end($results) : $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行行为
|
||||
* @access public
|
||||
* @param mixed $class 行为
|
||||
* @param mixed $params 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function exec($class, $params = null)
|
||||
{
|
||||
if ($class instanceof \Closure || is_array($class)) {
|
||||
$method = $class;
|
||||
} else {
|
||||
if (isset($this->bind[$class])) {
|
||||
$class = $this->bind[$class];
|
||||
}
|
||||
$method = [$class, self::$portal];
|
||||
}
|
||||
|
||||
return $this->app->invoke($method, [$params]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行某个标签的行为
|
||||
* @access protected
|
||||
* @param mixed $class 要执行的行为
|
||||
* @param string $tag 方法名(标签名)
|
||||
* @param mixed $params 参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function execTag($class, $tag = '', $params = null)
|
||||
{
|
||||
$method = Loader::parseName($tag, 1, false);
|
||||
|
||||
if ($class instanceof \Closure) {
|
||||
$call = $class;
|
||||
$class = 'Closure';
|
||||
} elseif (is_array($class) || strpos($class, '::')) {
|
||||
$call = $class;
|
||||
} else {
|
||||
$obj = Container::get($class);
|
||||
|
||||
if (!is_callable([$obj, $method])) {
|
||||
$method = self::$portal;
|
||||
}
|
||||
|
||||
$call = [$class, $method];
|
||||
$class = $class . '->' . $method;
|
||||
}
|
||||
|
||||
$result = $this->app->invoke($call, [$params]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
284
Server/thinkphp/library/think/Lang.php
Normal file
284
Server/thinkphp/library/think/Lang.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Lang
|
||||
{
|
||||
/**
|
||||
* 多语言信息
|
||||
* @var array
|
||||
*/
|
||||
private $lang = [];
|
||||
|
||||
/**
|
||||
* 当前语言
|
||||
* @var string
|
||||
*/
|
||||
private $range = 'zh-cn';
|
||||
|
||||
/**
|
||||
* 多语言自动侦测变量名
|
||||
* @var string
|
||||
*/
|
||||
protected $langDetectVar = 'lang';
|
||||
|
||||
/**
|
||||
* 多语言cookie变量
|
||||
* @var string
|
||||
*/
|
||||
protected $langCookieVar = 'think_var';
|
||||
|
||||
/**
|
||||
* 允许的多语言列表
|
||||
* @var array
|
||||
*/
|
||||
protected $allowLangList = [];
|
||||
|
||||
/**
|
||||
* Accept-Language转义为对应语言包名称 系统默认配置
|
||||
* @var string
|
||||
*/
|
||||
protected $acceptLanguage = [
|
||||
'zh-hans-cn' => 'zh-cn',
|
||||
];
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
// 设定当前的语言
|
||||
public function range($range = '')
|
||||
{
|
||||
if ('' == $range) {
|
||||
return $this->range;
|
||||
} else {
|
||||
$this->range = $range;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param string|array $name 语言变量
|
||||
* @param string $value 语言值
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public function set($name, $value = null, $range = '')
|
||||
{
|
||||
$range = $range ?: $this->range;
|
||||
// 批量定义
|
||||
if (!isset($this->lang[$range])) {
|
||||
$this->lang[$range] = [];
|
||||
}
|
||||
|
||||
if (is_array($name)) {
|
||||
return $this->lang[$range] = array_change_key_case($name) + $this->lang[$range];
|
||||
}
|
||||
|
||||
return $this->lang[$range][strtolower($name)] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param string|array $file 语言文件
|
||||
* @param string $range 语言作用域
|
||||
* @return array
|
||||
*/
|
||||
public function load($file, $range = '')
|
||||
{
|
||||
$range = $range ?: $this->range;
|
||||
if (!isset($this->lang[$range])) {
|
||||
$this->lang[$range] = [];
|
||||
}
|
||||
|
||||
// 批量定义
|
||||
if (is_string($file)) {
|
||||
$file = [$file];
|
||||
}
|
||||
|
||||
$lang = [];
|
||||
|
||||
foreach ($file as $_file) {
|
||||
if (is_file($_file)) {
|
||||
// 记录加载信息
|
||||
$this->app->log('[ LANG ] ' . $_file);
|
||||
$_lang = include $_file;
|
||||
if (is_array($_lang)) {
|
||||
$lang = array_change_key_case($_lang) + $lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($lang)) {
|
||||
$this->lang[$range] = $lang + $this->lang[$range];
|
||||
}
|
||||
|
||||
return $this->lang[$range];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param string|null $name 语言变量
|
||||
* @param string $range 语言作用域
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name, $range = '')
|
||||
{
|
||||
$range = $range ?: $this->range;
|
||||
|
||||
return isset($this->lang[$range][strtolower($name)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param string|null $name 语言变量
|
||||
* @param array $vars 变量替换
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name = null, $vars = [], $range = '')
|
||||
{
|
||||
$range = $range ?: $this->range;
|
||||
|
||||
// 空参数返回所有定义
|
||||
if (is_null($name)) {
|
||||
return $this->lang[$range];
|
||||
}
|
||||
|
||||
$key = strtolower($name);
|
||||
$value = isset($this->lang[$range][$key]) ? $this->lang[$range][$key] : $name;
|
||||
|
||||
// 变量解析
|
||||
if (!empty($vars) && is_array($vars)) {
|
||||
/**
|
||||
* Notes:
|
||||
* 为了检测的方便,数字索引的判断仅仅是参数数组的第一个元素的key为数字0
|
||||
* 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数
|
||||
*/
|
||||
if (key($vars) === 0) {
|
||||
// 数字索引解析
|
||||
array_unshift($vars, $value);
|
||||
$value = call_user_func_array('sprintf', $vars);
|
||||
} else {
|
||||
// 关联索引解析
|
||||
$replace = array_keys($vars);
|
||||
foreach ($replace as &$v) {
|
||||
$v = "{:{$v}}";
|
||||
}
|
||||
$value = str_replace($replace, $vars, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动侦测设置获取语言选择
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function detect()
|
||||
{
|
||||
// 自动侦测设置获取语言选择
|
||||
$langSet = '';
|
||||
|
||||
if (isset($_GET[$this->langDetectVar])) {
|
||||
// url中设置了语言变量
|
||||
$langSet = strtolower($_GET[$this->langDetectVar]);
|
||||
} elseif (isset($_COOKIE[$this->langCookieVar])) {
|
||||
// Cookie中设置了语言变量
|
||||
$langSet = strtolower($_COOKIE[$this->langCookieVar]);
|
||||
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
// 自动侦测浏览器语言
|
||||
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
|
||||
$langSet = strtolower($matches[1]);
|
||||
if (isset($this->acceptLanguage[$langSet])) {
|
||||
$langSet = $this->acceptLanguage[$langSet];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->allowLangList) || in_array($langSet, $this->allowLangList)) {
|
||||
// 合法的语言
|
||||
$this->range = $langSet ?: $this->range;
|
||||
}
|
||||
|
||||
return $this->range;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前语言到Cookie
|
||||
* @access public
|
||||
* @param string $lang 语言
|
||||
* @return void
|
||||
*/
|
||||
public function saveToCookie($lang = null)
|
||||
{
|
||||
$range = $lang ?: $this->range;
|
||||
|
||||
$_COOKIE[$this->langCookieVar] = $range;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言自动侦测的变量
|
||||
* @access public
|
||||
* @param string $var 变量名称
|
||||
* @return void
|
||||
*/
|
||||
public function setLangDetectVar($var)
|
||||
{
|
||||
$this->langDetectVar = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言的cookie保存变量
|
||||
* @access public
|
||||
* @param string $var 变量名称
|
||||
* @return void
|
||||
*/
|
||||
public function setLangCookieVar($var)
|
||||
{
|
||||
$this->langCookieVar = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置允许的语言列表
|
||||
* @access public
|
||||
* @param array $list 语言列表
|
||||
* @return void
|
||||
*/
|
||||
public function setAllowLangList(array $list)
|
||||
{
|
||||
$this->allowLangList = $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置转义的语言列表
|
||||
* @access public
|
||||
* @param array $list 语言列表
|
||||
* @return void
|
||||
*/
|
||||
public function setAcceptLanguage(array $list)
|
||||
{
|
||||
$this->acceptLanguage = array_merge($this->acceptLanguage, $list);
|
||||
}
|
||||
}
|
||||
417
Server/thinkphp/library/think/Loader.php
Normal file
417
Server/thinkphp/library/think/Loader.php
Normal file
@@ -0,0 +1,417 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\exception\ClassNotFoundException;
|
||||
|
||||
class Loader
|
||||
{
|
||||
/**
|
||||
* 类名映射信息
|
||||
* @var array
|
||||
*/
|
||||
protected static $classMap = [];
|
||||
|
||||
/**
|
||||
* 类库别名
|
||||
* @var array
|
||||
*/
|
||||
protected static $classAlias = [];
|
||||
|
||||
/**
|
||||
* PSR-4
|
||||
* @var array
|
||||
*/
|
||||
private static $prefixLengthsPsr4 = [];
|
||||
private static $prefixDirsPsr4 = [];
|
||||
private static $fallbackDirsPsr4 = [];
|
||||
|
||||
/**
|
||||
* PSR-0
|
||||
* @var array
|
||||
*/
|
||||
private static $prefixesPsr0 = [];
|
||||
private static $fallbackDirsPsr0 = [];
|
||||
|
||||
/**
|
||||
* 需要加载的文件
|
||||
* @var array
|
||||
*/
|
||||
private static $files = [];
|
||||
|
||||
/**
|
||||
* Composer安装路径
|
||||
* @var string
|
||||
*/
|
||||
private static $composerPath;
|
||||
|
||||
// 获取应用根目录
|
||||
public static function getRootPath()
|
||||
{
|
||||
if ('cli' == PHP_SAPI) {
|
||||
$scriptName = realpath($_SERVER['argv'][0]);
|
||||
} else {
|
||||
$scriptName = $_SERVER['SCRIPT_FILENAME'];
|
||||
}
|
||||
|
||||
$path = realpath(dirname($scriptName));
|
||||
|
||||
if (!is_file($path . DIRECTORY_SEPARATOR . 'think')) {
|
||||
$path = dirname($path);
|
||||
}
|
||||
|
||||
return $path . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
// 注册自动加载机制
|
||||
public static function register($autoload = '')
|
||||
{
|
||||
// 注册系统自动加载
|
||||
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
|
||||
|
||||
$rootPath = self::getRootPath();
|
||||
|
||||
self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;
|
||||
|
||||
// Composer自动加载支持
|
||||
if (is_dir(self::$composerPath)) {
|
||||
if (is_file(self::$composerPath . 'autoload_static.php')) {
|
||||
require self::$composerPath . 'autoload_static.php';
|
||||
|
||||
$declaredClass = get_declared_classes();
|
||||
$composerClass = array_pop($declaredClass);
|
||||
|
||||
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
|
||||
if (property_exists($composerClass, $attr)) {
|
||||
self::${$attr} = $composerClass::${$attr};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self::registerComposerLoader(self::$composerPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册命名空间定义
|
||||
self::addNamespace([
|
||||
'think' => __DIR__,
|
||||
'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
|
||||
]);
|
||||
|
||||
// 加载类库映射文件
|
||||
if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
|
||||
self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
|
||||
}
|
||||
|
||||
// 自动加载extend目录
|
||||
self::addAutoLoadDir($rootPath . 'extend');
|
||||
}
|
||||
|
||||
// 自动加载
|
||||
public static function autoload($class)
|
||||
{
|
||||
if (isset(self::$classAlias[$class])) {
|
||||
return class_alias(self::$classAlias[$class], $class);
|
||||
}
|
||||
|
||||
if ($file = self::findFile($class)) {
|
||||
|
||||
// Win环境严格区分大小写
|
||||
if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
__include_file($file);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找文件
|
||||
* @access private
|
||||
* @param string $class
|
||||
* @return string|false
|
||||
*/
|
||||
private static function findFile($class)
|
||||
{
|
||||
if (!empty(self::$classMap[$class])) {
|
||||
// 类库映射
|
||||
return self::$classMap[$class];
|
||||
}
|
||||
|
||||
// 查找 PSR-4
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
|
||||
|
||||
$first = $class[0];
|
||||
if (isset(self::$prefixLengthsPsr4[$first])) {
|
||||
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-4 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr4 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-0
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
|
||||
}
|
||||
|
||||
if (isset(self::$prefixesPsr0[$first])) {
|
||||
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-0 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr0 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return self::$classMap[$class] = false;
|
||||
}
|
||||
|
||||
// 注册classmap
|
||||
public static function addClassMap($class, $map = '')
|
||||
{
|
||||
if (is_array($class)) {
|
||||
self::$classMap = array_merge(self::$classMap, $class);
|
||||
} else {
|
||||
self::$classMap[$class] = $map;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册命名空间
|
||||
public static function addNamespace($namespace, $path = '')
|
||||
{
|
||||
if (is_array($namespace)) {
|
||||
foreach ($namespace as $prefix => $paths) {
|
||||
self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true);
|
||||
}
|
||||
} else {
|
||||
self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加Ps0空间
|
||||
private static function addPsr0($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
self::$fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
self::$fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
self::$fallbackDirsPsr0 = array_merge(
|
||||
self::$fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset(self::$prefixesPsr0[$first][$prefix])) {
|
||||
self::$prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
self::$prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
self::$prefixesPsr0[$first][$prefix] = array_merge(
|
||||
self::$prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加Psr4空间
|
||||
private static function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
self::$fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
self::$fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
self::$fallbackDirsPsr4 = array_merge(
|
||||
self::$fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
|
||||
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
self::$prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
self::$prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
self::$prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
self::$prefixDirsPsr4[$prefix] = array_merge(
|
||||
self::$prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册自动加载类库目录
|
||||
public static function addAutoLoadDir($path)
|
||||
{
|
||||
self::$fallbackDirsPsr4[] = $path;
|
||||
}
|
||||
|
||||
// 注册类别名
|
||||
public static function addClassAlias($alias, $class = null)
|
||||
{
|
||||
if (is_array($alias)) {
|
||||
self::$classAlias = array_merge(self::$classAlias, $alias);
|
||||
} else {
|
||||
self::$classAlias[$alias] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册composer自动加载
|
||||
public static function registerComposerLoader($composerPath)
|
||||
{
|
||||
if (is_file($composerPath . 'autoload_namespaces.php')) {
|
||||
$map = require $composerPath . 'autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
self::addPsr0($namespace, $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file($composerPath . 'autoload_psr4.php')) {
|
||||
$map = require $composerPath . 'autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
self::addPsr4($namespace, $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file($composerPath . 'autoload_classmap.php')) {
|
||||
$classMap = require $composerPath . 'autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
self::addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file($composerPath . 'autoload_files.php')) {
|
||||
self::$files = require $composerPath . 'autoload_files.php';
|
||||
}
|
||||
}
|
||||
|
||||
// 加载composer autofile文件
|
||||
public static function loadComposerAutoloadFiles()
|
||||
{
|
||||
foreach (self::$files as $fileIdentifier => $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
__require_file($file);
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串命名风格转换
|
||||
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
|
||||
* @access public
|
||||
* @param string $name 字符串
|
||||
* @param integer $type 转换类型
|
||||
* @param bool $ucfirst 首字母是否大写(驼峰规则)
|
||||
* @return string
|
||||
*/
|
||||
public static function parseName($name, $type = 0, $ucfirst = true)
|
||||
{
|
||||
if ($type) {
|
||||
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
|
||||
return strtoupper($match[1]);
|
||||
}, $name);
|
||||
return $ucfirst ? ucfirst($name) : lcfirst($name);
|
||||
}
|
||||
|
||||
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建工厂对象实例
|
||||
* @access public
|
||||
* @param string $name 工厂类名
|
||||
* @param string $namespace 默认命名空间
|
||||
* @return mixed
|
||||
*/
|
||||
public static function factory($name, $namespace = '', ...$args)
|
||||
{
|
||||
$class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name);
|
||||
|
||||
if (class_exists($class)) {
|
||||
return Container::getInstance()->invokeClass($class, $args);
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 作用范围隔离
|
||||
*
|
||||
* @param $file
|
||||
* @return mixed
|
||||
*/
|
||||
function __include_file($file)
|
||||
{
|
||||
return include $file;
|
||||
}
|
||||
|
||||
function __require_file($file)
|
||||
{
|
||||
return require $file;
|
||||
}
|
||||
389
Server/thinkphp/library/think/Log.php
Normal file
389
Server/thinkphp/library/think/Log.php
Normal file
@@ -0,0 +1,389 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Log implements LoggerInterface
|
||||
{
|
||||
const EMERGENCY = 'emergency';
|
||||
const ALERT = 'alert';
|
||||
const CRITICAL = 'critical';
|
||||
const ERROR = 'error';
|
||||
const WARNING = 'warning';
|
||||
const NOTICE = 'notice';
|
||||
const INFO = 'info';
|
||||
const DEBUG = 'debug';
|
||||
const SQL = 'sql';
|
||||
|
||||
/**
|
||||
* 日志信息
|
||||
* @var array
|
||||
*/
|
||||
protected $log = [];
|
||||
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 日志写入驱动
|
||||
* @var object
|
||||
*/
|
||||
protected $driver;
|
||||
|
||||
/**
|
||||
* 日志授权key
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* 是否允许日志写入
|
||||
* @var bool
|
||||
*/
|
||||
protected $allowWrite = true;
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
return (new static($app))->init($config->pull('log'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志初始化
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return $this
|
||||
*/
|
||||
public function init($config = [])
|
||||
{
|
||||
$type = isset($config['type']) ? $config['type'] : 'File';
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
unset($config['type']);
|
||||
|
||||
if (!empty($config['close'])) {
|
||||
$this->allowWrite = false;
|
||||
}
|
||||
|
||||
$this->driver = Loader::factory($type, '\\think\\log\\driver\\', $config);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志信息
|
||||
* @access public
|
||||
* @param string $type 信息类型
|
||||
* @return array
|
||||
*/
|
||||
public function getLog($type = '')
|
||||
{
|
||||
return $type ? $this->log[$type] : $this->log;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录日志信息
|
||||
* @access public
|
||||
* @param mixed $msg 日志信息
|
||||
* @param string $type 日志级别
|
||||
* @param array $context 替换内容
|
||||
* @return $this
|
||||
*/
|
||||
public function record($msg, $type = 'info', array $context = [])
|
||||
{
|
||||
if (!$this->allowWrite) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_string($msg) && !empty($context)) {
|
||||
$replace = [];
|
||||
foreach ($context as $key => $val) {
|
||||
$replace['{' . $key . '}'] = $val;
|
||||
}
|
||||
|
||||
$msg = strtr($msg, $replace);
|
||||
}
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
if (empty($this->config['level']) || in_array($type, $this->config['level'])) {
|
||||
// 命令行日志实时写入
|
||||
$this->write($msg, $type, true);
|
||||
}
|
||||
} else {
|
||||
$this->log[$type][] = $msg;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空日志信息
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->log = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前日志记录的授权key
|
||||
* @access public
|
||||
* @param string $key 授权key
|
||||
* @return $this
|
||||
*/
|
||||
public function key($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查日志写入权限
|
||||
* @access public
|
||||
* @param array $config 当前日志配置参数
|
||||
* @return bool
|
||||
*/
|
||||
public function check($config)
|
||||
{
|
||||
if ($this->key && !empty($config['allow_key']) && !in_array($this->key, $config['allow_key'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭本次请求日志写入
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->allowWrite = false;
|
||||
$this->log = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存调试信息
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if (empty($this->log) || !$this->allowWrite) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->check($this->config)) {
|
||||
// 检测日志写入权限
|
||||
return false;
|
||||
}
|
||||
|
||||
$log = [];
|
||||
|
||||
foreach ($this->log as $level => $info) {
|
||||
if (!$this->app->isDebug() && 'debug' == $level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($this->config['level']) || in_array($level, $this->config['level'])) {
|
||||
$log[$level] = $info;
|
||||
|
||||
$this->app['hook']->listen('log_level', [$level, $info]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->driver->save($log, true);
|
||||
|
||||
if ($result) {
|
||||
$this->log = [];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实时写入日志信息 并支持行为
|
||||
* @access public
|
||||
* @param mixed $msg 调试信息
|
||||
* @param string $type 日志级别
|
||||
* @param bool $force 是否强制写入
|
||||
* @return bool
|
||||
*/
|
||||
public function write($msg, $type = 'info', $force = false)
|
||||
{
|
||||
// 封装日志信息
|
||||
if (empty($this->config['level'])) {
|
||||
$force = true;
|
||||
}
|
||||
|
||||
if (true === $force || in_array($type, $this->config['level'])) {
|
||||
$log[$type][] = $msg;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 监听log_write
|
||||
$this->app['hook']->listen('log_write', $log);
|
||||
|
||||
// 写入日志
|
||||
return $this->driver->save($log, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录日志信息
|
||||
* @access public
|
||||
* @param string $level 日志级别
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function log($level, $message, array $context = [])
|
||||
{
|
||||
$this->record($message, $level, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录emergency信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function emergency($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录警报信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function alert($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录紧急情况
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function critical($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function error($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录warning信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function warning($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录notice信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function notice($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录一般信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function info($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录调试信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function debug($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录sql信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function sql($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
205
Server/thinkphp/library/think/Middleware.php
Normal file
205
Server/thinkphp/library/think/Middleware.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Slince <taosikai@yeah.net>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
class Middleware
|
||||
{
|
||||
protected $queue = [];
|
||||
protected $app;
|
||||
protected $config = [
|
||||
'default_namespace' => 'app\\http\\middleware\\',
|
||||
];
|
||||
|
||||
public function __construct(App $app, array $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
return new static($app, $config->pull('middleware'));
|
||||
}
|
||||
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入中间件
|
||||
* @access public
|
||||
* @param array $middlewares
|
||||
* @param string $type 中间件类型
|
||||
*/
|
||||
public function import(array $middlewares = [], $type = 'route')
|
||||
{
|
||||
foreach ($middlewares as $middleware) {
|
||||
$this->add($middleware, $type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册中间件
|
||||
* @access public
|
||||
* @param mixed $middleware
|
||||
* @param string $type 中间件类型
|
||||
*/
|
||||
public function add($middleware, $type = 'route')
|
||||
{
|
||||
if (is_null($middleware)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$middleware = $this->buildMiddleware($middleware, $type);
|
||||
|
||||
if ($middleware) {
|
||||
$this->queue[$type][] = $middleware;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册控制器中间件
|
||||
* @access public
|
||||
* @param mixed $middleware
|
||||
*/
|
||||
public function controller($middleware)
|
||||
{
|
||||
return $this->add($middleware, 'controller');
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除中间件
|
||||
* @access public
|
||||
* @param mixed $middleware
|
||||
* @param string $type 中间件类型
|
||||
*/
|
||||
public function unshift($middleware, $type = 'route')
|
||||
{
|
||||
if (is_null($middleware)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$middleware = $this->buildMiddleware($middleware, $type);
|
||||
|
||||
if ($middleware) {
|
||||
array_unshift($this->queue[$type], $middleware);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注册的中间件
|
||||
* @access public
|
||||
* @param string $type 中间件类型
|
||||
*/
|
||||
public function all($type = 'route')
|
||||
{
|
||||
return $this->queue[$type] ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除中间件
|
||||
* @access public
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间件调度
|
||||
* @access public
|
||||
* @param Request $request
|
||||
* @param string $type 中间件类型
|
||||
*/
|
||||
public function dispatch(Request $request, $type = 'route')
|
||||
{
|
||||
return call_user_func($this->resolve($type), $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析中间件
|
||||
* @access protected
|
||||
* @param mixed $middleware
|
||||
* @param string $type 中间件类型
|
||||
*/
|
||||
protected function buildMiddleware($middleware, $type = 'route')
|
||||
{
|
||||
if (is_array($middleware)) {
|
||||
list($middleware, $param) = $middleware;
|
||||
}
|
||||
|
||||
if ($middleware instanceof \Closure) {
|
||||
return [$middleware, isset($param) ? $param : null];
|
||||
}
|
||||
|
||||
if (!is_string($middleware)) {
|
||||
throw new InvalidArgumentException('The middleware is invalid');
|
||||
}
|
||||
|
||||
if (false === strpos($middleware, '\\')) {
|
||||
if (isset($this->config[$middleware])) {
|
||||
$middleware = $this->config[$middleware];
|
||||
} else {
|
||||
$middleware = $this->config['default_namespace'] . $middleware;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($middleware)) {
|
||||
return $this->import($middleware, $type);
|
||||
}
|
||||
|
||||
if (strpos($middleware, ':')) {
|
||||
list($middleware, $param) = explode(':', $middleware, 2);
|
||||
}
|
||||
|
||||
return [[$this->app->make($middleware), 'handle'], isset($param) ? $param : null];
|
||||
}
|
||||
|
||||
protected function resolve($type = 'route')
|
||||
{
|
||||
return function (Request $request) use ($type) {
|
||||
|
||||
$middleware = array_shift($this->queue[$type]);
|
||||
|
||||
if (null === $middleware) {
|
||||
throw new InvalidArgumentException('The queue was exhausted, with no response returned');
|
||||
}
|
||||
|
||||
list($call, $param) = $middleware;
|
||||
|
||||
try {
|
||||
$response = call_user_func_array($call, [$request, $this->resolve($type), $param]);
|
||||
} catch (HttpResponseException $exception) {
|
||||
$response = $exception->getResponse();
|
||||
}
|
||||
|
||||
if (!$response instanceof Response) {
|
||||
throw new LogicException('The middleware must return Response instance');
|
||||
}
|
||||
|
||||
return $response;
|
||||
};
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
1125
Server/thinkphp/library/think/Model.php
Normal file
1125
Server/thinkphp/library/think/Model.php
Normal file
File diff suppressed because it is too large
Load Diff
445
Server/thinkphp/library/think/Paginator.php
Normal file
445
Server/thinkphp/library/think/Paginator.php
Normal file
@@ -0,0 +1,445 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use Traversable;
|
||||
|
||||
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
/**
|
||||
* 是否简洁模式
|
||||
* @var bool
|
||||
*/
|
||||
protected $simple = false;
|
||||
|
||||
/**
|
||||
* 数据集
|
||||
* @var Collection
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
* @var integer
|
||||
*/
|
||||
protected $currentPage;
|
||||
|
||||
/**
|
||||
* 最后一页
|
||||
* @var integer
|
||||
*/
|
||||
protected $lastPage;
|
||||
|
||||
/**
|
||||
* 数据总数
|
||||
* @var integer|null
|
||||
*/
|
||||
protected $total;
|
||||
|
||||
/**
|
||||
* 每页数量
|
||||
* @var integer
|
||||
*/
|
||||
protected $listRows;
|
||||
|
||||
/**
|
||||
* 是否有下一页
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasMore;
|
||||
|
||||
/**
|
||||
* 分页配置
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'var_page' => 'page',
|
||||
'path' => '/',
|
||||
'query' => [],
|
||||
'fragment' => '',
|
||||
];
|
||||
|
||||
public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
$this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
|
||||
|
||||
$this->simple = $simple;
|
||||
$this->listRows = $listRows;
|
||||
|
||||
if (!$items instanceof Collection) {
|
||||
$items = Collection::make($items);
|
||||
}
|
||||
|
||||
if ($simple) {
|
||||
$this->currentPage = $this->setCurrentPage($currentPage);
|
||||
$this->hasMore = count($items) > ($this->listRows);
|
||||
$items = $items->slice(0, $this->listRows);
|
||||
} else {
|
||||
$this->total = $total;
|
||||
$this->lastPage = (int) ceil($total / $listRows);
|
||||
$this->currentPage = $this->setCurrentPage($currentPage);
|
||||
$this->hasMore = $this->currentPage < $this->lastPage;
|
||||
}
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @param $items
|
||||
* @param $listRows
|
||||
* @param null $currentPage
|
||||
* @param null $total
|
||||
* @param bool $simple
|
||||
* @param array $options
|
||||
* @return Paginator
|
||||
*/
|
||||
public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
|
||||
{
|
||||
return new static($items, $listRows, $currentPage, $total, $simple, $options);
|
||||
}
|
||||
|
||||
protected function setCurrentPage($currentPage)
|
||||
{
|
||||
if (!$this->simple && $currentPage > $this->lastPage) {
|
||||
return $this->lastPage > 0 ? $this->lastPage : 1;
|
||||
}
|
||||
|
||||
return $currentPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页码对应的链接
|
||||
*
|
||||
* @access protected
|
||||
* @param $page
|
||||
* @return string
|
||||
*/
|
||||
protected function url($page)
|
||||
{
|
||||
if ($page <= 0) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
if (strpos($this->options['path'], '[PAGE]') === false) {
|
||||
$parameters = [$this->options['var_page'] => $page];
|
||||
$path = $this->options['path'];
|
||||
} else {
|
||||
$parameters = [];
|
||||
$path = str_replace('[PAGE]', $page, $this->options['path']);
|
||||
}
|
||||
|
||||
if (count($this->options['query']) > 0) {
|
||||
$parameters = array_merge($this->options['query'], $parameters);
|
||||
}
|
||||
|
||||
$url = $path;
|
||||
if (!empty($parameters)) {
|
||||
$url .= '?' . http_build_query($parameters, null, '&');
|
||||
}
|
||||
|
||||
return $url . $this->buildFragment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动获取当前页码
|
||||
* @access public
|
||||
* @param string $varPage
|
||||
* @param int $default
|
||||
* @return int
|
||||
*/
|
||||
public static function getCurrentPage($varPage = 'page', $default = 1)
|
||||
{
|
||||
$page = Container::get('request')->param($varPage);
|
||||
|
||||
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
|
||||
return $page;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动获取当前的path
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentPath()
|
||||
{
|
||||
return Container::get('request')->baseUrl();
|
||||
}
|
||||
|
||||
public function total()
|
||||
{
|
||||
if ($this->simple) {
|
||||
throw new \DomainException('not support total');
|
||||
}
|
||||
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
public function listRows()
|
||||
{
|
||||
return $this->listRows;
|
||||
}
|
||||
|
||||
public function currentPage()
|
||||
{
|
||||
return $this->currentPage;
|
||||
}
|
||||
|
||||
public function lastPage()
|
||||
{
|
||||
if ($this->simple) {
|
||||
throw new \DomainException('not support last');
|
||||
}
|
||||
|
||||
return $this->lastPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据是否足够分页
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPages()
|
||||
{
|
||||
return !(1 == $this->currentPage && !$this->hasMore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一组分页链接
|
||||
*
|
||||
* @access public
|
||||
* @param int $start
|
||||
* @param int $end
|
||||
* @return array
|
||||
*/
|
||||
public function getUrlRange($start, $end)
|
||||
{
|
||||
$urls = [];
|
||||
|
||||
for ($page = $start; $page <= $end; $page++) {
|
||||
$urls[$page] = $this->url($page);
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置URL锚点
|
||||
*
|
||||
* @access public
|
||||
* @param string|null $fragment
|
||||
* @return $this
|
||||
*/
|
||||
public function fragment($fragment)
|
||||
{
|
||||
$this->options['fragment'] = $fragment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加URL参数
|
||||
*
|
||||
* @access public
|
||||
* @param array|string $key
|
||||
* @param string|null $value
|
||||
* @return $this
|
||||
*/
|
||||
public function appends($key, $value = null)
|
||||
{
|
||||
if (!is_array($key)) {
|
||||
$queries = [$key => $value];
|
||||
} else {
|
||||
$queries = $key;
|
||||
}
|
||||
|
||||
foreach ($queries as $k => $v) {
|
||||
if ($k !== $this->options['var_page']) {
|
||||
$this->options['query'][$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造锚点字符串
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
protected function buildFragment()
|
||||
{
|
||||
return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染分页html
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function render();
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->items->all();
|
||||
}
|
||||
|
||||
public function getCollection()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->items->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 给每个元素执行个回调
|
||||
*
|
||||
* @access public
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function each(callable $callback)
|
||||
{
|
||||
foreach ($this->items as $key => $item) {
|
||||
$result = $callback($item, $key);
|
||||
|
||||
if (false === $result) {
|
||||
break;
|
||||
} elseif (!is_object($item)) {
|
||||
$this->items[$key] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
* @access public
|
||||
* @return Traversable An instance of an object implementing <b>Iterator</b> or
|
||||
* <b>Traversable</b>
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->items->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->items->offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items->offsetGet($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->items->offsetSet($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->items->offsetUnset($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->items->count();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->render();
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
try {
|
||||
$total = $this->total();
|
||||
} catch (\DomainException $e) {
|
||||
$total = null;
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'per_page' => $this->listRows(),
|
||||
'current_page' => $this->currentPage(),
|
||||
'last_page' => $this->lastPage,
|
||||
'data' => $this->items->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
$collection = $this->getCollection();
|
||||
|
||||
$result = call_user_func_array([$collection, $name], $arguments);
|
||||
|
||||
if ($result === $collection) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
1268
Server/thinkphp/library/think/Process.php
Normal file
1268
Server/thinkphp/library/think/Process.php
Normal file
File diff suppressed because it is too large
Load Diff
2267
Server/thinkphp/library/think/Request.php
Normal file
2267
Server/thinkphp/library/think/Request.php
Normal file
File diff suppressed because it is too large
Load Diff
429
Server/thinkphp/library/think/Response.php
Normal file
429
Server/thinkphp/library/think/Response.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\response\Redirect as RedirectResponse;
|
||||
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
* 原始数据
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* 应用对象实例
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 当前contentType
|
||||
* @var string
|
||||
*/
|
||||
protected $contentType = 'text/html';
|
||||
|
||||
/**
|
||||
* 字符集
|
||||
* @var string
|
||||
*/
|
||||
protected $charset = 'utf-8';
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 200;
|
||||
|
||||
/**
|
||||
* 是否允许请求缓存
|
||||
* @var bool
|
||||
*/
|
||||
protected $allowCache = true;
|
||||
|
||||
/**
|
||||
* 输出参数
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* header参数
|
||||
* @var array
|
||||
*/
|
||||
protected $header = [];
|
||||
|
||||
/**
|
||||
* 输出内容
|
||||
* @var string
|
||||
*/
|
||||
protected $content = null;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
*/
|
||||
public function __construct($data = '', $code = 200, array $header = [], $options = [])
|
||||
{
|
||||
$this->data($data);
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
$this->contentType($this->contentType, $this->charset);
|
||||
|
||||
$this->code = $code;
|
||||
$this->app = Container::get('app');
|
||||
$this->header = array_merge($this->header, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Response对象
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param string $type 输出类型
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
* @return Response
|
||||
*/
|
||||
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = [])
|
||||
{
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type));
|
||||
|
||||
if (class_exists($class)) {
|
||||
return new $class($data, $code, $header, $options);
|
||||
}
|
||||
|
||||
return new static($data, $code, $header, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据到客户端
|
||||
* @access public
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
// 监听response_send
|
||||
$this->app['hook']->listen('response_send', $this);
|
||||
|
||||
// 处理输出数据
|
||||
$data = $this->getContent();
|
||||
|
||||
// Trace调试注入
|
||||
if ('cli' != PHP_SAPI && $this->app['env']->get('app_trace', $this->app->config('app.app_trace'))) {
|
||||
$this->app['debug']->inject($this, $data);
|
||||
}
|
||||
|
||||
if (200 == $this->code && $this->allowCache) {
|
||||
$cache = $this->app['request']->getCache();
|
||||
if ($cache) {
|
||||
$this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate';
|
||||
$this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT';
|
||||
$this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT';
|
||||
|
||||
$this->app['cache']->tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!headers_sent() && !empty($this->header)) {
|
||||
// 发送状态码
|
||||
http_response_code($this->code);
|
||||
// 发送头部信息
|
||||
foreach ($this->header as $name => $val) {
|
||||
header($name . (!is_null($val) ? ':' . $val : ''));
|
||||
}
|
||||
}
|
||||
|
||||
$this->sendData($data);
|
||||
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
// 提高页面响应
|
||||
fastcgi_finish_request();
|
||||
}
|
||||
|
||||
// 监听response_end
|
||||
$this->app['hook']->listen('response_end', $this);
|
||||
|
||||
// 清空当次请求有效的数据
|
||||
if (!($this instanceof RedirectResponse)) {
|
||||
$this->app['session']->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据
|
||||
* @access protected
|
||||
* @param mixed $data 要处理的数据
|
||||
* @return mixed
|
||||
*/
|
||||
protected function output($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出数据
|
||||
* @access protected
|
||||
* @param string $data 要处理的数据
|
||||
* @return void
|
||||
*/
|
||||
protected function sendData($data)
|
||||
{
|
||||
echo $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出的参数
|
||||
* @access public
|
||||
* @param mixed $options 输出参数
|
||||
* @return $this
|
||||
*/
|
||||
public function options($options = [])
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出数据设置
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @return $this
|
||||
*/
|
||||
public function data($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许请求缓存
|
||||
* @access public
|
||||
* @param bool $cache 允许请求缓存
|
||||
* @return $this
|
||||
*/
|
||||
public function allowCache($cache)
|
||||
{
|
||||
$this->allowCache = $cache;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @param string $value 参数值
|
||||
* @return $this
|
||||
*/
|
||||
public function header($name, $value = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->header = array_merge($this->header, $name);
|
||||
} else {
|
||||
$this->header[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面输出内容
|
||||
* @access public
|
||||
* @param mixed $content
|
||||
* @return $this
|
||||
*/
|
||||
public function content($content)
|
||||
{
|
||||
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
|
||||
$content,
|
||||
'__toString',
|
||||
])
|
||||
) {
|
||||
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
|
||||
}
|
||||
|
||||
$this->content = (string) $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTTP状态
|
||||
* @access public
|
||||
* @param integer $code 状态码
|
||||
* @return $this
|
||||
*/
|
||||
public function code($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* LastModified
|
||||
* @access public
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function lastModified($time)
|
||||
{
|
||||
$this->header['Last-Modified'] = $time;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expires
|
||||
* @access public
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function expires($time)
|
||||
{
|
||||
$this->header['Expires'] = $time;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* ETag
|
||||
* @access public
|
||||
* @param string $eTag
|
||||
* @return $this
|
||||
*/
|
||||
public function eTag($eTag)
|
||||
{
|
||||
$this->header['ETag'] = $eTag;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面缓存控制
|
||||
* @access public
|
||||
* @param string $cache 缓存设置
|
||||
* @return $this
|
||||
*/
|
||||
public function cacheControl($cache)
|
||||
{
|
||||
$this->header['Cache-control'] = $cache;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面不做任何缓存
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function noCache()
|
||||
{
|
||||
$this->header['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0';
|
||||
$this->header['Pragma'] = 'no-cache';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面输出类型
|
||||
* @access public
|
||||
* @param string $contentType 输出类型
|
||||
* @param string $charset 输出编码
|
||||
* @return $this
|
||||
*/
|
||||
public function contentType($contentType, $charset = 'utf-8')
|
||||
{
|
||||
$this->header['Content-Type'] = $contentType . '; charset=' . $charset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头部信息
|
||||
* @access public
|
||||
* @param string $name 头部名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHeader($name = '')
|
||||
{
|
||||
if (!empty($name)) {
|
||||
return isset($this->header[$name]) ? $this->header[$name] : null;
|
||||
}
|
||||
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始数据
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出数据
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null == $this->content) {
|
||||
$content = $this->output($this->data);
|
||||
|
||||
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
|
||||
$content,
|
||||
'__toString',
|
||||
])
|
||||
) {
|
||||
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
|
||||
}
|
||||
|
||||
$this->content = (string) $content;
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态码
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
992
Server/thinkphp/library/think/Route.php
Normal file
992
Server/thinkphp/library/think/Route.php
Normal file
@@ -0,0 +1,992 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\exception\RouteNotFoundException;
|
||||
use think\route\AliasRule;
|
||||
use think\route\Dispatch;
|
||||
use think\route\dispatch\Url as UrlDispatch;
|
||||
use think\route\Domain;
|
||||
use think\route\Resource;
|
||||
use think\route\Rule;
|
||||
use think\route\RuleGroup;
|
||||
use think\route\RuleItem;
|
||||
|
||||
class Route
|
||||
{
|
||||
/**
|
||||
* REST定义
|
||||
* @var array
|
||||
*/
|
||||
protected $rest = [
|
||||
'index' => ['get', '', 'index'],
|
||||
'create' => ['get', '/create', 'create'],
|
||||
'edit' => ['get', '/<id>/edit', 'edit'],
|
||||
'read' => ['get', '/<id>', 'read'],
|
||||
'save' => ['post', '', 'save'],
|
||||
'update' => ['put', '/<id>', 'update'],
|
||||
'delete' => ['delete', '/<id>', 'delete'],
|
||||
];
|
||||
|
||||
/**
|
||||
* 请求方法前缀定义
|
||||
* @var array
|
||||
*/
|
||||
protected $methodPrefix = [
|
||||
'get' => 'get',
|
||||
'post' => 'post',
|
||||
'put' => 'put',
|
||||
'delete' => 'delete',
|
||||
'patch' => 'patch',
|
||||
];
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 请求对象
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 当前HOST
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* 当前域名
|
||||
* @var string
|
||||
*/
|
||||
protected $domain;
|
||||
|
||||
/**
|
||||
* 当前分组对象
|
||||
* @var RuleGroup
|
||||
*/
|
||||
protected $group;
|
||||
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 路由绑定
|
||||
* @var array
|
||||
*/
|
||||
protected $bind = [];
|
||||
|
||||
/**
|
||||
* 域名对象
|
||||
* @var array
|
||||
*/
|
||||
protected $domains = [];
|
||||
|
||||
/**
|
||||
* 跨域路由规则
|
||||
* @var RuleGroup
|
||||
*/
|
||||
protected $cross;
|
||||
|
||||
/**
|
||||
* 路由别名
|
||||
* @var array
|
||||
*/
|
||||
protected $alias = [];
|
||||
|
||||
/**
|
||||
* 路由是否延迟解析
|
||||
* @var bool
|
||||
*/
|
||||
protected $lazy = true;
|
||||
|
||||
/**
|
||||
* 路由是否测试模式
|
||||
* @var bool
|
||||
*/
|
||||
protected $isTest;
|
||||
|
||||
/**
|
||||
* (分组)路由规则是否合并解析
|
||||
* @var bool
|
||||
*/
|
||||
protected $mergeRuleRegex = true;
|
||||
|
||||
/**
|
||||
* 路由解析自动搜索多级控制器
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoSearchController = true;
|
||||
|
||||
public function __construct(App $app, array $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->request = $app['request'];
|
||||
$this->config = $config;
|
||||
|
||||
$this->host = $this->request->host(true) ?: $config['app_host'];
|
||||
|
||||
$this->setDefaultDomain();
|
||||
}
|
||||
|
||||
public function config($name = null)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
return isset($this->config[$name]) ? $this->config[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig(array $config = [])
|
||||
{
|
||||
$this->config = array_merge($this->config, array_change_key_case($config));
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
$config = $config->pull('app');
|
||||
$route = new static($app, $config);
|
||||
|
||||
$route->lazy($config['url_lazy_route'])
|
||||
->autoSearchController($config['controller_auto_search'])
|
||||
->mergeRuleRegex($config['route_rule_merge']);
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路由的请求对象实例
|
||||
* @access public
|
||||
* @param Request $request 请求对象实例
|
||||
* @return void
|
||||
*/
|
||||
public function setRequest($request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路由域名及分组(包括资源路由)是否延迟解析
|
||||
* @access public
|
||||
* @param bool $lazy 路由是否延迟解析
|
||||
* @return $this
|
||||
*/
|
||||
public function lazy($lazy = true)
|
||||
{
|
||||
$this->lazy = $lazy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路由为测试模式
|
||||
* @access public
|
||||
* @param bool $test 路由是否测试模式
|
||||
* @return void
|
||||
*/
|
||||
public function setTestMode($test)
|
||||
{
|
||||
$this->isTest = $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查路由是否为测试模式
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isTest()
|
||||
{
|
||||
return $this->isTest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路由域名及分组(包括资源路由)是否合并解析
|
||||
* @access public
|
||||
* @param bool $merge 路由是否合并解析
|
||||
* @return $this
|
||||
*/
|
||||
public function mergeRuleRegex($merge = true)
|
||||
{
|
||||
$this->mergeRuleRegex = $merge;
|
||||
$this->group->mergeRuleRegex($merge);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路由自动解析是否搜索多级控制器
|
||||
* @access public
|
||||
* @param bool $auto 是否自动搜索多级控制器
|
||||
* @return $this
|
||||
*/
|
||||
public function autoSearchController($auto = true)
|
||||
{
|
||||
$this->autoSearchController = $auto;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化默认域名
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function setDefaultDomain()
|
||||
{
|
||||
// 默认域名
|
||||
$this->domain = $this->host;
|
||||
|
||||
// 注册默认域名
|
||||
$domain = new Domain($this, $this->host);
|
||||
|
||||
$this->domains[$this->host] = $domain;
|
||||
|
||||
// 默认分组
|
||||
$this->group = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前域名
|
||||
* @access public
|
||||
* @param RuleGroup $group 域名
|
||||
* @return void
|
||||
*/
|
||||
public function setGroup(RuleGroup $group)
|
||||
{
|
||||
$this->group = $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前分组
|
||||
* @access public
|
||||
* @return RuleGroup
|
||||
*/
|
||||
public function getGroup()
|
||||
{
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册变量规则
|
||||
* @access public
|
||||
* @param string|array $name 变量名
|
||||
* @param string $rule 变量规则
|
||||
* @return $this
|
||||
*/
|
||||
public function pattern($name, $rule = '')
|
||||
{
|
||||
$this->group->pattern($name, $rule);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册路由参数
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @param mixed $value 值
|
||||
* @return $this
|
||||
*/
|
||||
public function option($name, $value = '')
|
||||
{
|
||||
$this->group->option($name, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册域名路由
|
||||
* @access public
|
||||
* @param string|array $name 子域名
|
||||
* @param mixed $rule 路由规则
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return Domain
|
||||
*/
|
||||
public function domain($name, $rule = '', $option = [], $pattern = [])
|
||||
{
|
||||
// 支持多个域名使用相同路由规则
|
||||
$domainName = is_array($name) ? array_shift($name) : $name;
|
||||
|
||||
if ('*' != $domainName && false === strpos($domainName, '.')) {
|
||||
$domainName .= '.' . $this->request->rootDomain();
|
||||
}
|
||||
|
||||
if (!isset($this->domains[$domainName])) {
|
||||
$domain = (new Domain($this, $domainName, $rule, $option, $pattern))
|
||||
->lazy($this->lazy)
|
||||
->mergeRuleRegex($this->mergeRuleRegex);
|
||||
|
||||
$this->domains[$domainName] = $domain;
|
||||
} else {
|
||||
$domain = $this->domains[$domainName];
|
||||
$domain->parseGroupRule($rule);
|
||||
}
|
||||
|
||||
if (is_array($name) && !empty($name)) {
|
||||
$root = $this->request->rootDomain();
|
||||
foreach ($name as $item) {
|
||||
if (false === strpos($item, '.')) {
|
||||
$item .= '.' . $root;
|
||||
}
|
||||
|
||||
$this->domains[$item] = $domainName;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回域名对象
|
||||
return $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDomains()
|
||||
{
|
||||
return $this->domains;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路由绑定
|
||||
* @access public
|
||||
* @param string $bind 绑定信息
|
||||
* @param string $domain 域名
|
||||
* @return $this
|
||||
*/
|
||||
public function bind($bind, $domain = null)
|
||||
{
|
||||
$domain = is_null($domain) ? $this->domain : $domain;
|
||||
|
||||
$this->bind[$domain] = $bind;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取路由绑定
|
||||
* @access public
|
||||
* @param string $domain 域名
|
||||
* @return string|null
|
||||
*/
|
||||
public function getBind($domain = null)
|
||||
{
|
||||
if (is_null($domain)) {
|
||||
$domain = $this->domain;
|
||||
} elseif (true === $domain) {
|
||||
return $this->bind;
|
||||
} elseif (false === strpos($domain, '.')) {
|
||||
$domain .= '.' . $this->request->rootDomain();
|
||||
}
|
||||
|
||||
$subDomain = $this->request->subDomain();
|
||||
|
||||
if (strpos($subDomain, '.')) {
|
||||
$name = '*' . strstr($subDomain, '.');
|
||||
}
|
||||
|
||||
if (isset($this->bind[$domain])) {
|
||||
$result = $this->bind[$domain];
|
||||
} elseif (isset($name) && isset($this->bind[$name])) {
|
||||
$result = $this->bind[$name];
|
||||
} elseif (!empty($subDomain) && isset($this->bind['*'])) {
|
||||
$result = $this->bind['*'];
|
||||
} else {
|
||||
$result = null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取路由标识
|
||||
* @access public
|
||||
* @param string $name 路由标识
|
||||
* @param string $domain 域名
|
||||
* @return mixed
|
||||
*/
|
||||
public function getName($name = null, $domain = null, $method = '*')
|
||||
{
|
||||
return $this->app['rule_name']->get($name, $domain, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param string $domain 域名
|
||||
* @return array
|
||||
*/
|
||||
public function getRule($rule, $domain = null)
|
||||
{
|
||||
if (is_null($domain)) {
|
||||
$domain = $this->domain;
|
||||
}
|
||||
|
||||
return $this->app['rule_name']->getRule($rule, $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取路由
|
||||
* @access public
|
||||
* @param string $domain 域名
|
||||
* @return array
|
||||
*/
|
||||
public function getRuleList($domain = null)
|
||||
{
|
||||
return $this->app['rule_name']->getRuleList($domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量导入路由标识
|
||||
* @access public
|
||||
* @param array $name 路由标识
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->app['rule_name']->import($name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入配置文件的路由规则
|
||||
* @access public
|
||||
* @param array $rules 路由规则
|
||||
* @param string $type 请求类型
|
||||
* @return void
|
||||
*/
|
||||
public function import(array $rules, $type = '*')
|
||||
{
|
||||
// 检查域名部署
|
||||
if (isset($rules['__domain__'])) {
|
||||
foreach ($rules['__domain__'] as $key => $rule) {
|
||||
$this->domain($key, $rule);
|
||||
}
|
||||
unset($rules['__domain__']);
|
||||
}
|
||||
|
||||
// 检查变量规则
|
||||
if (isset($rules['__pattern__'])) {
|
||||
$this->pattern($rules['__pattern__']);
|
||||
unset($rules['__pattern__']);
|
||||
}
|
||||
|
||||
// 检查路由别名
|
||||
if (isset($rules['__alias__'])) {
|
||||
foreach ($rules['__alias__'] as $key => $val) {
|
||||
$this->alias($key, $val);
|
||||
}
|
||||
unset($rules['__alias__']);
|
||||
}
|
||||
|
||||
// 检查资源路由
|
||||
if (isset($rules['__rest__'])) {
|
||||
foreach ($rules['__rest__'] as $key => $rule) {
|
||||
$this->resource($key, $rule);
|
||||
}
|
||||
unset($rules['__rest__']);
|
||||
}
|
||||
|
||||
// 检查路由规则(包含分组)
|
||||
foreach ($rules as $key => $val) {
|
||||
if (is_numeric($key)) {
|
||||
$key = array_shift($val);
|
||||
}
|
||||
|
||||
if (empty($val)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_string($key) && 0 === strpos($key, '[')) {
|
||||
$key = substr($key, 1, -1);
|
||||
$this->group($key, $val);
|
||||
} elseif (is_array($val)) {
|
||||
$this->rule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []);
|
||||
} else {
|
||||
$this->rule($key, $val, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册路由规则
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param string $method 请求类型
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function rule($rule, $route, $method = '*', array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->group->addRule($rule, $route, $method, $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置跨域有效路由规则
|
||||
* @access public
|
||||
* @param Rule $rule 路由规则
|
||||
* @param string $method 请求类型
|
||||
* @return $this
|
||||
*/
|
||||
public function setCrossDomainRule($rule, $method = '*')
|
||||
{
|
||||
if (!isset($this->cross)) {
|
||||
$this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
|
||||
}
|
||||
|
||||
$this->cross->addRuleItem($rule, $method);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量注册路由规则
|
||||
* @access public
|
||||
* @param array $rules 路由规则
|
||||
* @param string $method 请求类型
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return void
|
||||
*/
|
||||
public function rules($rules, $method = '*', array $option = [], array $pattern = [])
|
||||
{
|
||||
$this->group->addRules($rules, $method, $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册路由分组
|
||||
* @access public
|
||||
* @param string|array $name 分组名称或者参数
|
||||
* @param array|\Closure $route 分组路由
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleGroup
|
||||
*/
|
||||
public function group($name, $route, array $option = [], array $pattern = [])
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$option = $name;
|
||||
$name = isset($option['name']) ? $option['name'] : '';
|
||||
}
|
||||
|
||||
return (new RuleGroup($this, $this->group, $name, $route, $option, $pattern))
|
||||
->lazy($this->lazy)
|
||||
->mergeRuleRegex($this->mergeRuleRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function any($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $route, '*', $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册GET路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function get($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $route, 'GET', $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册POST路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function post($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $route, 'POST', $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册PUT路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function put($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $route, 'PUT', $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册DELETE路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function delete($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $route, 'DELETE', $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册PATCH路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param mixed $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function patch($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $route, 'PATCH', $option, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册资源路由
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param string $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return Resource
|
||||
*/
|
||||
public function resource($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
return (new Resource($this, $this->group, $rule, $route, $option, $pattern, $this->rest))
|
||||
->lazy($this->lazy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册控制器路由 操作方法对应不同的请求前缀
|
||||
* @access public
|
||||
* @param string $rule 路由规则
|
||||
* @param string $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleGroup
|
||||
*/
|
||||
public function controller($rule, $route = '', array $option = [], array $pattern = [])
|
||||
{
|
||||
$group = new RuleGroup($this, $this->group, $rule, null, $option, $pattern);
|
||||
|
||||
foreach ($this->methodPrefix as $type => $val) {
|
||||
$group->addRule('<action>', $val . '<action>', $type);
|
||||
}
|
||||
|
||||
return $group->prefix($route . '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册视图路由
|
||||
* @access public
|
||||
* @param string|array $rule 路由规则
|
||||
* @param string $template 路由模板地址
|
||||
* @param array $vars 模板变量
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function view($rule, $template = '', array $vars = [], array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $template, 'GET', $option, $pattern)->view($vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册重定向路由
|
||||
* @access public
|
||||
* @param string|array $rule 路由规则
|
||||
* @param string $route 路由地址
|
||||
* @param array $status 状态码
|
||||
* @param array $option 路由参数
|
||||
* @param array $pattern 变量规则
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function redirect($rule, $route = '', $status = 301, array $option = [], array $pattern = [])
|
||||
{
|
||||
return $this->rule($rule, $route, '*', $option, $pattern)->redirect()->status($status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册别名路由
|
||||
* @access public
|
||||
* @param string $rule 路由别名
|
||||
* @param string $route 路由地址
|
||||
* @param array $option 路由参数
|
||||
* @return AliasRule
|
||||
*/
|
||||
public function alias($rule, $route, array $option = [])
|
||||
{
|
||||
$aliasRule = new AliasRule($this, $this->group, $rule, $route, $option);
|
||||
|
||||
$this->alias[$rule] = $aliasRule;
|
||||
|
||||
return $aliasRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取别名路由定义
|
||||
* @access public
|
||||
* @param string $name 路由别名
|
||||
* @return string|array|null
|
||||
*/
|
||||
public function getAlias($name = null)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
return isset($this->alias[$name]) ? $this->alias[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置不同请求类型下面的方法前缀
|
||||
* @access public
|
||||
* @param string|array $method 请求类型
|
||||
* @param string $prefix 类型前缀
|
||||
* @return $this
|
||||
*/
|
||||
public function setMethodPrefix($method, $prefix = '')
|
||||
{
|
||||
if (is_array($method)) {
|
||||
$this->methodPrefix = array_merge($this->methodPrefix, array_change_key_case($method));
|
||||
} else {
|
||||
$this->methodPrefix[strtolower($method)] = $prefix;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求类型的方法前缀
|
||||
* @access public
|
||||
* @param string $method 请求类型
|
||||
* @param string $prefix 类型前缀
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMethodPrefix($method)
|
||||
{
|
||||
$method = strtolower($method);
|
||||
|
||||
return isset($this->methodPrefix[$method]) ? $this->methodPrefix[$method] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* rest方法定义和修改
|
||||
* @access public
|
||||
* @param string $name 方法名称
|
||||
* @param array|bool $resource 资源
|
||||
* @return $this
|
||||
*/
|
||||
public function rest($name, $resource = [])
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->rest = $resource ? $name : array_merge($this->rest, $name);
|
||||
} else {
|
||||
$this->rest[$name] = $resource;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取rest方法定义的参数
|
||||
* @access public
|
||||
* @param string $name 方法名称
|
||||
* @return array|null
|
||||
*/
|
||||
public function getRest($name = null)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
return $this->rest;
|
||||
}
|
||||
|
||||
return isset($this->rest[$name]) ? $this->rest[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册未匹配路由规则后的处理
|
||||
* @access public
|
||||
* @param string $route 路由地址
|
||||
* @param string $method 请求类型
|
||||
* @param array $option 路由参数
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function miss($route, $method = '*', array $option = [])
|
||||
{
|
||||
return $this->group->addMissRule($route, $method, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个自动解析的URL路由
|
||||
* @access public
|
||||
* @param string $route 路由地址
|
||||
* @return RuleItem
|
||||
*/
|
||||
public function auto($route)
|
||||
{
|
||||
return $this->group->addAutoRule($route);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测URL路由
|
||||
* @access public
|
||||
* @param string $url URL地址
|
||||
* @param bool $must 是否强制路由
|
||||
* @return Dispatch
|
||||
* @throws RouteNotFoundException
|
||||
*/
|
||||
public function check($url, $must = false)
|
||||
{
|
||||
// 自动检测域名路由
|
||||
$domain = $this->checkDomain();
|
||||
$url = str_replace($this->config['pathinfo_depr'], '|', $url);
|
||||
|
||||
$completeMatch = $this->config['route_complete_match'];
|
||||
|
||||
$result = $domain->check($this->request, $url, $completeMatch);
|
||||
|
||||
if (false === $result && !empty($this->cross)) {
|
||||
// 检测跨域路由
|
||||
$result = $this->cross->check($this->request, $url, $completeMatch);
|
||||
}
|
||||
|
||||
if (false !== $result) {
|
||||
// 路由匹配
|
||||
return $result;
|
||||
} elseif ($must) {
|
||||
// 强制路由不匹配则抛出异常
|
||||
throw new RouteNotFoundException();
|
||||
}
|
||||
|
||||
// 默认路由解析
|
||||
return new UrlDispatch($this->request, $this->group, $url, [
|
||||
'auto_search' => $this->autoSearchController,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测域名的路由规则
|
||||
* @access protected
|
||||
* @return Domain
|
||||
*/
|
||||
protected function checkDomain()
|
||||
{
|
||||
// 获取当前子域名
|
||||
$subDomain = $this->request->subDomain();
|
||||
|
||||
$item = false;
|
||||
|
||||
if ($subDomain && count($this->domains) > 1) {
|
||||
$domain = explode('.', $subDomain);
|
||||
$domain2 = array_pop($domain);
|
||||
|
||||
if ($domain) {
|
||||
// 存在三级域名
|
||||
$domain3 = array_pop($domain);
|
||||
}
|
||||
|
||||
if ($subDomain && isset($this->domains[$subDomain])) {
|
||||
// 子域名配置
|
||||
$item = $this->domains[$subDomain];
|
||||
} elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
|
||||
// 泛三级域名
|
||||
$item = $this->domains['*.' . $domain2];
|
||||
$panDomain = $domain3;
|
||||
} elseif (isset($this->domains['*']) && !empty($domain2)) {
|
||||
// 泛二级域名
|
||||
if ('www' != $domain2) {
|
||||
$item = $this->domains['*'];
|
||||
$panDomain = $domain2;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($panDomain)) {
|
||||
// 保存当前泛域名
|
||||
$this->request->setPanDomain($panDomain);
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $item) {
|
||||
// 检测当前完整域名
|
||||
$item = $this->domains[$this->host];
|
||||
}
|
||||
|
||||
if (is_string($item)) {
|
||||
$item = $this->domains[$item];
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空路由规则
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->app['rule_name']->clear();
|
||||
$this->group->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置全局的路由分组参数
|
||||
* @access public
|
||||
* @param string $method 方法名
|
||||
* @param array $args 调用参数
|
||||
* @return RuleGroup
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array([$this->group, $method], $args);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app'], $data['request']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
579
Server/thinkphp/library/think/Session.php
Normal file
579
Server/thinkphp/library/think/Session.php
Normal file
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\exception\ClassNotFoundException;
|
||||
|
||||
class Session
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix = '';
|
||||
|
||||
/**
|
||||
* 是否初始化
|
||||
* @var bool
|
||||
*/
|
||||
protected $init = null;
|
||||
|
||||
/**
|
||||
* 锁驱动
|
||||
* @var object
|
||||
*/
|
||||
protected $lockDriver = null;
|
||||
|
||||
/**
|
||||
* 锁key
|
||||
* @var string
|
||||
*/
|
||||
protected $sessKey = 'PHPSESSID';
|
||||
|
||||
/**
|
||||
* 锁超时时间
|
||||
* @var integer
|
||||
*/
|
||||
protected $lockTimeout = 3;
|
||||
|
||||
/**
|
||||
* 是否启用锁机制
|
||||
* @var bool
|
||||
*/
|
||||
protected $lock = false;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置或者获取session作用域(前缀)
|
||||
* @access public
|
||||
* @param string $prefix
|
||||
* @return string|void
|
||||
*/
|
||||
public function prefix($prefix = '')
|
||||
{
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
if (empty($prefix) && null !== $prefix) {
|
||||
return $this->prefix;
|
||||
} else {
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
public static function __make(Config $config)
|
||||
{
|
||||
return new static($config->pull('session'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig(array $config = [])
|
||||
{
|
||||
$this->config = array_merge($this->config, array_change_key_case($config));
|
||||
|
||||
if (isset($config['prefix'])) {
|
||||
$this->prefix = $config['prefix'];
|
||||
}
|
||||
|
||||
if (isset($config['use_lock'])) {
|
||||
$this->lock = $config['use_lock'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置已经初始化
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function inited()
|
||||
{
|
||||
$this->init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* session初始化
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$config = $config ?: $this->config;
|
||||
|
||||
$isDoStart = false;
|
||||
if (isset($config['use_trans_sid'])) {
|
||||
ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0);
|
||||
}
|
||||
|
||||
// 启动session
|
||||
if (!empty($config['auto_start']) && PHP_SESSION_ACTIVE != session_status()) {
|
||||
ini_set('session.auto_start', 0);
|
||||
$isDoStart = true;
|
||||
}
|
||||
|
||||
if (isset($config['prefix'])) {
|
||||
$this->prefix = $config['prefix'];
|
||||
}
|
||||
|
||||
if (isset($config['use_lock'])) {
|
||||
$this->lock = $config['use_lock'];
|
||||
}
|
||||
|
||||
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {
|
||||
session_id($_REQUEST[$config['var_session_id']]);
|
||||
} elseif (isset($config['id']) && !empty($config['id'])) {
|
||||
session_id($config['id']);
|
||||
}
|
||||
|
||||
if (isset($config['name'])) {
|
||||
session_name($config['name']);
|
||||
}
|
||||
|
||||
if (isset($config['path'])) {
|
||||
session_save_path($config['path']);
|
||||
}
|
||||
|
||||
if (isset($config['domain'])) {
|
||||
ini_set('session.cookie_domain', $config['domain']);
|
||||
}
|
||||
|
||||
if (isset($config['expire'])) {
|
||||
ini_set('session.gc_maxlifetime', $config['expire']);
|
||||
ini_set('session.cookie_lifetime', $config['expire']);
|
||||
}
|
||||
|
||||
if (isset($config['secure'])) {
|
||||
ini_set('session.cookie_secure', $config['secure']);
|
||||
}
|
||||
|
||||
if (isset($config['httponly'])) {
|
||||
ini_set('session.cookie_httponly', $config['httponly']);
|
||||
}
|
||||
|
||||
if (isset($config['use_cookies'])) {
|
||||
ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0);
|
||||
}
|
||||
|
||||
if (isset($config['cache_limiter'])) {
|
||||
session_cache_limiter($config['cache_limiter']);
|
||||
}
|
||||
|
||||
if (isset($config['cache_expire'])) {
|
||||
session_cache_expire($config['cache_expire']);
|
||||
}
|
||||
|
||||
if (!empty($config['type'])) {
|
||||
// 读取session驱动
|
||||
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
|
||||
|
||||
// 检查驱动类
|
||||
if (!class_exists($class) || !session_set_save_handler(new $class($config))) {
|
||||
throw new ClassNotFoundException('error session handler:' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
if ($isDoStart) {
|
||||
$this->start();
|
||||
} else {
|
||||
$this->init = false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* session自动启动或者初始化
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if (is_null($this->init)) {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
if (false === $this->init) {
|
||||
if (PHP_SESSION_ACTIVE != session_status()) {
|
||||
$this->start();
|
||||
}
|
||||
$this->init = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session设置
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public function set($name, $value, $prefix = null)
|
||||
{
|
||||
$this->lock();
|
||||
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
if (strpos($name, '.')) {
|
||||
// 二维数组赋值
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
if ($prefix) {
|
||||
$_SESSION[$prefix][$name1][$name2] = $value;
|
||||
} else {
|
||||
$_SESSION[$name1][$name2] = $value;
|
||||
}
|
||||
} elseif ($prefix) {
|
||||
$_SESSION[$prefix][$name] = $value;
|
||||
} else {
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
|
||||
$this->unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* session获取
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name = '', $prefix = null)
|
||||
{
|
||||
$this->lock();
|
||||
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
|
||||
|
||||
if ('' != $name) {
|
||||
$name = explode('.', $name);
|
||||
|
||||
foreach ($name as $val) {
|
||||
if (isset($value[$val])) {
|
||||
$value = $value[$val];
|
||||
} else {
|
||||
$value = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->unlock();
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* session 读写锁驱动实例化
|
||||
*/
|
||||
protected function initDriver()
|
||||
{
|
||||
$config = $this->config;
|
||||
|
||||
if (!empty($config['type']) && isset($config['use_lock']) && $config['use_lock']) {
|
||||
// 读取session驱动
|
||||
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
|
||||
|
||||
// 检查驱动类及类中是否存在 lock 和 unlock 函数
|
||||
if (class_exists($class) && method_exists($class, 'lock') && method_exists($class, 'unlock')) {
|
||||
$this->lockDriver = new $class($config);
|
||||
}
|
||||
}
|
||||
|
||||
// 通过cookie获得session_id
|
||||
if (isset($config['name']) && $config['name']) {
|
||||
$this->sessKey = $config['name'];
|
||||
}
|
||||
|
||||
if (isset($config['lock_timeout']) && $config['lock_timeout'] > 0) {
|
||||
$this->lockTimeout = $config['lock_timeout'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session 读写加锁
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function lock()
|
||||
{
|
||||
if (empty($this->lock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->initDriver();
|
||||
|
||||
if (null !== $this->lockDriver && method_exists($this->lockDriver, 'lock')) {
|
||||
$t = time();
|
||||
// 使用 session_id 作为互斥条件,即只对同一 session_id 的会话互斥。第一次请求没有 session_id
|
||||
$sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : '';
|
||||
|
||||
do {
|
||||
if (time() - $t > $this->lockTimeout) {
|
||||
$this->unlock();
|
||||
}
|
||||
} while (!$this->lockDriver->lock($sessID, $this->lockTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session 读写解锁
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function unlock()
|
||||
{
|
||||
if (empty($this->lock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pause();
|
||||
|
||||
if ($this->lockDriver && method_exists($this->lockDriver, 'unlock')) {
|
||||
$sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : '';
|
||||
$this->lockDriver->unlock($sessID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session获取并删除
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return mixed
|
||||
*/
|
||||
public function pull($name, $prefix = null)
|
||||
{
|
||||
$result = $this->get($name, $prefix);
|
||||
|
||||
if ($result) {
|
||||
$this->delete($name, $prefix);
|
||||
return $result;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session设置 下一次请求有效
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public function flash($name, $value)
|
||||
{
|
||||
$this->set($name, $value);
|
||||
|
||||
if (!$this->has('__flash__.__time__')) {
|
||||
$this->set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
|
||||
}
|
||||
|
||||
$this->push('__flash__', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空当前请求的session数据
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
if (!$this->init) {
|
||||
return;
|
||||
}
|
||||
|
||||
$item = $this->get('__flash__');
|
||||
|
||||
if (!empty($item)) {
|
||||
$time = $item['__time__'];
|
||||
|
||||
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
|
||||
unset($item['__time__']);
|
||||
$this->delete($item);
|
||||
$this->set('__flash__', []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除session数据
|
||||
* @access public
|
||||
* @param string|array $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public function delete($name, $prefix = null)
|
||||
{
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $key) {
|
||||
$this->delete($key, $prefix);
|
||||
}
|
||||
} elseif (strpos($name, '.')) {
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
if ($prefix) {
|
||||
unset($_SESSION[$prefix][$name1][$name2]);
|
||||
} else {
|
||||
unset($_SESSION[$name1][$name2]);
|
||||
}
|
||||
} else {
|
||||
if ($prefix) {
|
||||
unset($_SESSION[$prefix][$name]);
|
||||
} else {
|
||||
unset($_SESSION[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空session数据
|
||||
* @access public
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public function clear($prefix = null)
|
||||
{
|
||||
empty($this->init) && $this->boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
if ($prefix) {
|
||||
unset($_SESSION[$prefix]);
|
||||
} else {
|
||||
$_SESSION = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断session数据
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name, $prefix = null)
|
||||
{
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
|
||||
|
||||
$name = explode('.', $name);
|
||||
|
||||
foreach ($name as $val) {
|
||||
if (!isset($value[$val])) {
|
||||
return false;
|
||||
} else {
|
||||
$value = $value[$val];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据到一个session数组
|
||||
* @access public
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function push($key, $value)
|
||||
{
|
||||
$array = $this->get($key);
|
||||
|
||||
if (is_null($array)) {
|
||||
$array = [];
|
||||
}
|
||||
|
||||
$array[] = $value;
|
||||
|
||||
$this->set($key, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动session
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
session_start();
|
||||
|
||||
$this->init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁session
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function destroy()
|
||||
{
|
||||
if (!empty($_SESSION)) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
session_unset();
|
||||
session_destroy();
|
||||
|
||||
$this->init = null;
|
||||
$this->lockDriver = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新生成session_id
|
||||
* @access public
|
||||
* @param bool $delete 是否删除关联会话文件
|
||||
* @return void
|
||||
*/
|
||||
public function regenerate($delete = false)
|
||||
{
|
||||
session_regenerate_id($delete);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停session
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function pause()
|
||||
{
|
||||
// 暂停session
|
||||
session_write_close();
|
||||
$this->init = false;
|
||||
}
|
||||
}
|
||||
1318
Server/thinkphp/library/think/Template.php
Normal file
1318
Server/thinkphp/library/think/Template.php
Normal file
File diff suppressed because it is too large
Load Diff
412
Server/thinkphp/library/think/Url.php
Normal file
412
Server/thinkphp/library/think/Url.php
Normal file
@@ -0,0 +1,412 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Url
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* ROOT地址
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* 绑定检查
|
||||
* @var bool
|
||||
*/
|
||||
protected $bindCheck;
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app, array $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
|
||||
if (is_file($app->getRuntimePath() . 'route.php')) {
|
||||
// 读取路由映射文件
|
||||
$app['route']->setName(include $app->getRuntimePath() . 'route.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->config = array_merge($this->config, array_change_key_case($config));
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
return new static($app, $config->pull('app'));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL生成 支持路由反射
|
||||
* @access public
|
||||
* @param string $url 路由地址
|
||||
* @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2']
|
||||
* @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值
|
||||
* @param boolean|string $domain 是否显示域名 或者直接传入域名
|
||||
* @return string
|
||||
*/
|
||||
public function build($url = '', $vars = '', $suffix = true, $domain = false)
|
||||
{
|
||||
// 解析URL
|
||||
if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
|
||||
// [name] 表示使用路由命名标识生成URL
|
||||
$name = substr($url, 1, $pos - 1);
|
||||
$url = 'name' . substr($url, $pos + 1);
|
||||
}
|
||||
|
||||
if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
|
||||
$info = parse_url($url);
|
||||
$url = !empty($info['path']) ? $info['path'] : '';
|
||||
|
||||
if (isset($info['fragment'])) {
|
||||
// 解析锚点
|
||||
$anchor = $info['fragment'];
|
||||
|
||||
if (false !== strpos($anchor, '?')) {
|
||||
// 解析参数
|
||||
list($anchor, $info['query']) = explode('?', $anchor, 2);
|
||||
}
|
||||
|
||||
if (false !== strpos($anchor, '@')) {
|
||||
// 解析域名
|
||||
list($anchor, $domain) = explode('@', $anchor, 2);
|
||||
}
|
||||
} elseif (strpos($url, '@') && false === strpos($url, '\\')) {
|
||||
// 解析域名
|
||||
list($url, $domain) = explode('@', $url, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 解析参数
|
||||
if (is_string($vars)) {
|
||||
// aaa=1&bbb=2 转换成数组
|
||||
parse_str($vars, $vars);
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$checkName = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
|
||||
$checkDomain = $domain && is_string($domain) ? $domain : null;
|
||||
|
||||
$rule = $this->app['route']->getName($checkName, $checkDomain);
|
||||
|
||||
if (is_null($rule) && isset($info['query'])) {
|
||||
$rule = $this->app['route']->getName($url);
|
||||
// 解析地址里面参数 合并到vars
|
||||
parse_str($info['query'], $params);
|
||||
$vars = array_merge($params, $vars);
|
||||
unset($info['query']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
|
||||
// 匹配路由命名标识
|
||||
$url = $match[0];
|
||||
|
||||
if ($domain) {
|
||||
$domain = $match[1];
|
||||
}
|
||||
|
||||
if (!is_null($match[2])) {
|
||||
$suffix = $match[2];
|
||||
}
|
||||
} elseif (!empty($rule) && isset($name)) {
|
||||
throw new \InvalidArgumentException('route name not exists:' . $name);
|
||||
} else {
|
||||
// 检查别名路由
|
||||
$alias = $this->app['route']->getAlias();
|
||||
$matchAlias = false;
|
||||
|
||||
if ($alias) {
|
||||
// 别名路由解析
|
||||
foreach ($alias as $key => $item) {
|
||||
$val = $item->getRoute();
|
||||
|
||||
if (0 === strpos($url, $val)) {
|
||||
$url = $key . substr($url, strlen($val));
|
||||
$matchAlias = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$matchAlias) {
|
||||
// 路由标识不存在 直接解析
|
||||
$url = $this->parseUrl($url);
|
||||
}
|
||||
|
||||
// 检测URL绑定
|
||||
if (!$this->bindCheck) {
|
||||
$bind = $this->app['route']->getBind($domain && is_string($domain) ? $domain : null);
|
||||
|
||||
if ($bind && 0 === strpos($url, $bind)) {
|
||||
$url = substr($url, strlen($bind) + 1);
|
||||
} else {
|
||||
$binds = $this->app['route']->getBind(true);
|
||||
|
||||
foreach ($binds as $key => $val) {
|
||||
if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
|
||||
$url = substr($url, strlen($val) + 1);
|
||||
$domain = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($info['query'])) {
|
||||
// 解析地址里面参数 合并到vars
|
||||
parse_str($info['query'], $params);
|
||||
$vars = array_merge($params, $vars);
|
||||
}
|
||||
}
|
||||
|
||||
// 还原URL分隔符
|
||||
$depr = $this->config['pathinfo_depr'];
|
||||
$url = str_replace('/', $depr, $url);
|
||||
|
||||
// URL后缀
|
||||
if ('/' == substr($url, -1) || '' == $url) {
|
||||
$suffix = '';
|
||||
} else {
|
||||
$suffix = $this->parseSuffix($suffix);
|
||||
}
|
||||
|
||||
// 锚点
|
||||
$anchor = !empty($anchor) ? '#' . $anchor : '';
|
||||
|
||||
// 参数组装
|
||||
if (!empty($vars)) {
|
||||
// 添加参数
|
||||
if ($this->config['url_common_param']) {
|
||||
$vars = http_build_query($vars);
|
||||
$url .= $suffix . '?' . $vars . $anchor;
|
||||
} else {
|
||||
$paramType = $this->config['url_param_type'];
|
||||
|
||||
foreach ($vars as $var => $val) {
|
||||
if ('' !== trim($val)) {
|
||||
if ($paramType) {
|
||||
$url .= $depr . urlencode($val);
|
||||
} else {
|
||||
$url .= $depr . $var . $depr . urlencode($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
} else {
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
|
||||
// 检测域名
|
||||
$domain = $this->parseDomain($url, $domain);
|
||||
|
||||
// URL组装
|
||||
$url = $domain . rtrim($this->root ?: $this->app['request']->root(), '/') . '/' . ltrim($url, '/');
|
||||
|
||||
$this->bindCheck = false;
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 直接解析URL地址
|
||||
protected function parseUrl($url)
|
||||
{
|
||||
$request = $this->app['request'];
|
||||
|
||||
if (0 === strpos($url, '/')) {
|
||||
// 直接作为路由地址解析
|
||||
$url = substr($url, 1);
|
||||
} elseif (false !== strpos($url, '\\')) {
|
||||
// 解析到类
|
||||
$url = ltrim(str_replace('\\', '/', $url), '/');
|
||||
} elseif (0 === strpos($url, '@')) {
|
||||
// 解析到控制器
|
||||
$url = substr($url, 1);
|
||||
} else {
|
||||
// 解析到 模块/控制器/操作
|
||||
$module = $request->module();
|
||||
$module = $module ? $module . '/' : '';
|
||||
$controller = $request->controller();
|
||||
|
||||
if ('' == $url) {
|
||||
$action = $request->action();
|
||||
} else {
|
||||
$path = explode('/', $url);
|
||||
$action = array_pop($path);
|
||||
$controller = empty($path) ? $controller : array_pop($path);
|
||||
$module = empty($path) ? $module : array_pop($path) . '/';
|
||||
}
|
||||
|
||||
if ($this->config['url_convert']) {
|
||||
$action = strtolower($action);
|
||||
$controller = Loader::parseName($controller);
|
||||
}
|
||||
|
||||
$url = $module . $controller . '/' . $action;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 检测域名
|
||||
protected function parseDomain(&$url, $domain)
|
||||
{
|
||||
if (!$domain) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rootDomain = $this->app['request']->rootDomain();
|
||||
if (true === $domain) {
|
||||
// 自动判断域名
|
||||
$domain = $this->config['app_host'] ?: $this->app['request']->host();
|
||||
|
||||
$domains = $this->app['route']->getDomains();
|
||||
|
||||
if ($domains) {
|
||||
$route_domain = array_keys($domains);
|
||||
foreach ($route_domain as $domain_prefix) {
|
||||
if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
|
||||
foreach ($domains as $key => $rule) {
|
||||
$rule = is_array($rule) ? $rule[0] : $rule;
|
||||
if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
|
||||
$url = ltrim($url, $rule);
|
||||
$domain = $key;
|
||||
|
||||
// 生成对应子域名
|
||||
if (!empty($rootDomain)) {
|
||||
$domain .= $rootDomain;
|
||||
}
|
||||
break;
|
||||
} elseif (false !== strpos($key, '*')) {
|
||||
if (!empty($rootDomain)) {
|
||||
$domain .= $rootDomain;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (0 !== strpos($domain, $rootDomain) && false === strpos($domain, '.')) {
|
||||
$domain .= '.' . $rootDomain;
|
||||
}
|
||||
|
||||
if (false !== strpos($domain, '://')) {
|
||||
$scheme = '';
|
||||
} else {
|
||||
$scheme = $this->app['request']->isSsl() || $this->config['is_https'] ? 'https://' : 'http://';
|
||||
|
||||
}
|
||||
|
||||
return $scheme . $domain;
|
||||
}
|
||||
|
||||
// 解析URL后缀
|
||||
protected function parseSuffix($suffix)
|
||||
{
|
||||
if ($suffix) {
|
||||
$suffix = true === $suffix ? $this->config['url_html_suffix'] : $suffix;
|
||||
|
||||
if ($pos = strpos($suffix, '|')) {
|
||||
$suffix = substr($suffix, 0, $pos);
|
||||
}
|
||||
}
|
||||
|
||||
return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix;
|
||||
}
|
||||
|
||||
// 匹配路由地址
|
||||
public function getRuleUrl($rule, &$vars = [], $allowDomain = '')
|
||||
{
|
||||
$port = $this->app['request']->port();
|
||||
foreach ($rule as $item) {
|
||||
list($url, $pattern, $domain, $suffix, $method) = $item;
|
||||
|
||||
if (is_string($allowDomain) && $domain != $allowDomain) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($port && !in_array($port, [80, 443])) {
|
||||
$domain .= ':' . $port;
|
||||
}
|
||||
|
||||
if (empty($pattern)) {
|
||||
return [rtrim($url, '?/-'), $domain, $suffix];
|
||||
}
|
||||
|
||||
$type = $this->config['url_common_param'];
|
||||
$keys = [];
|
||||
|
||||
foreach ($pattern as $key => $val) {
|
||||
if (isset($vars[$key])) {
|
||||
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
|
||||
$keys[] = $key;
|
||||
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
|
||||
$result = [rtrim($url, '?/-'), $domain, $suffix];
|
||||
} elseif (2 == $val) {
|
||||
$url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
|
||||
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
|
||||
$result = [rtrim($url, '?/-'), $domain, $suffix];
|
||||
} else {
|
||||
$result = null;
|
||||
$keys = [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$vars = array_diff_key($vars, array_flip($keys));
|
||||
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 指定当前生成URL地址的root
|
||||
public function root($root)
|
||||
{
|
||||
$this->root = $root;
|
||||
$this->app['request']->setRoot($root);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
1556
Server/thinkphp/library/think/Validate.php
Normal file
1556
Server/thinkphp/library/think/Validate.php
Normal file
File diff suppressed because it is too large
Load Diff
253
Server/thinkphp/library/think/View.php
Normal file
253
Server/thinkphp/library/think/View.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class View
|
||||
{
|
||||
/**
|
||||
* 模板引擎实例
|
||||
* @var object
|
||||
*/
|
||||
public $engine;
|
||||
|
||||
/**
|
||||
* 模板变量
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* 内容过滤
|
||||
* @var mixed
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
/**
|
||||
* 全局模板变量
|
||||
* @var array
|
||||
*/
|
||||
protected static $var = [];
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param mixed $engine 模板引擎参数
|
||||
* @return $this
|
||||
*/
|
||||
public function init($engine = [])
|
||||
{
|
||||
// 初始化模板引擎
|
||||
$this->engine($engine);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function __make(Config $config)
|
||||
{
|
||||
return (new static())->init($config->pull('template'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量静态赋值
|
||||
* @access public
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @return $this
|
||||
*/
|
||||
public function share($name, $value = '')
|
||||
{
|
||||
if (is_array($name)) {
|
||||
self::$var = array_merge(self::$var, $name);
|
||||
} else {
|
||||
self::$var[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理模板变量
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
self::$var = [];
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access public
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @return $this
|
||||
*/
|
||||
public function assign($name, $value = '')
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->data = array_merge($this->data, $name);
|
||||
} else {
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前模板解析的引擎
|
||||
* @access public
|
||||
* @param array|string $options 引擎参数
|
||||
* @return $this
|
||||
*/
|
||||
public function engine($options = [])
|
||||
{
|
||||
if (is_string($options)) {
|
||||
$type = $options;
|
||||
$options = [];
|
||||
} else {
|
||||
$type = !empty($options['type']) ? $options['type'] : 'Think';
|
||||
}
|
||||
|
||||
if (isset($options['type'])) {
|
||||
unset($options['type']);
|
||||
}
|
||||
|
||||
$this->engine = Loader::factory($type, '\\think\\view\\driver\\', $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置模板引擎
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @param mixed $value 参数值
|
||||
* @return $this
|
||||
*/
|
||||
public function config($name, $value = null)
|
||||
{
|
||||
$this->engine->config($name, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查模板是否存在
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
return $this->engine->exists($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图过滤
|
||||
* @access public
|
||||
* @param Callable $filter 过滤方法或闭包
|
||||
* @return $this
|
||||
*/
|
||||
public function filter($filter)
|
||||
{
|
||||
if ($filter) {
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析和获取模板内容 用于输出
|
||||
* @access public
|
||||
* @param string $template 模板文件名或者内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $config 模板参数
|
||||
* @param bool $renderContent 是否渲染内容
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function fetch($template = '', $vars = [], $config = [], $renderContent = false)
|
||||
{
|
||||
// 模板变量
|
||||
$vars = array_merge(self::$var, $this->data, $vars);
|
||||
|
||||
// 页面缓存
|
||||
ob_start();
|
||||
ob_implicit_flush(0);
|
||||
|
||||
// 渲染输出
|
||||
try {
|
||||
$method = $renderContent ? 'display' : 'fetch';
|
||||
$this->engine->$method($template, $vars, $config);
|
||||
} catch (\Exception $e) {
|
||||
ob_end_clean();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// 获取并清空缓存
|
||||
$content = ob_get_clean();
|
||||
|
||||
if ($this->filter) {
|
||||
$content = call_user_func_array($this->filter, [$content]);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容输出
|
||||
* @access public
|
||||
* @param string $content 内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function display($content, $vars = [], $config = [])
|
||||
{
|
||||
return $this->fetch($content, $vars, $config, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access public
|
||||
* @param string $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得模板显示变量的值
|
||||
* @access protected
|
||||
* @param string $name 模板变量
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测模板变量是否设置
|
||||
* @access public
|
||||
* @param string $name 模板变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->data[$name]);
|
||||
}
|
||||
}
|
||||
366
Server/thinkphp/library/think/cache/Driver.php
vendored
Normal file
366
Server/thinkphp/library/think/cache/Driver.php
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache;
|
||||
|
||||
use think\Container;
|
||||
|
||||
/**
|
||||
* 缓存基础类
|
||||
*/
|
||||
abstract class Driver
|
||||
{
|
||||
/**
|
||||
* 驱动句柄
|
||||
* @var object
|
||||
*/
|
||||
protected $handler = null;
|
||||
|
||||
/**
|
||||
* 缓存读取次数
|
||||
* @var integer
|
||||
*/
|
||||
protected $readTimes = 0;
|
||||
|
||||
/**
|
||||
* 缓存写入次数
|
||||
* @var integer
|
||||
*/
|
||||
protected $writeTimes = 0;
|
||||
|
||||
/**
|
||||
* 缓存参数
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @var string
|
||||
*/
|
||||
protected $tag;
|
||||
|
||||
/**
|
||||
* 序列化方法
|
||||
* @var array
|
||||
*/
|
||||
protected static $serialize = ['serialize', 'unserialize', 'think_serialize:', 16];
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function has($name);
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function get($name, $default = false);
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function set($name, $value, $expire = null);
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
abstract public function inc($name, $step = 1);
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
abstract public function dec($name, $step = 1);
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function rm($name);
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function clear($tag = null);
|
||||
|
||||
/**
|
||||
* 获取有效期
|
||||
* @access protected
|
||||
* @param integer|\DateTime $expire 有效期
|
||||
* @return integer
|
||||
*/
|
||||
protected function getExpireTime($expire)
|
||||
{
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
|
||||
return $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的缓存标识
|
||||
* @access protected
|
||||
* @param string $name 缓存名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
return $this->options['prefix'] . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存并删除
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public function pull($name)
|
||||
{
|
||||
$result = $this->get($name, false);
|
||||
|
||||
if ($result) {
|
||||
$this->rm($name);
|
||||
return $result;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不存在则写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return mixed
|
||||
*/
|
||||
public function remember($name, $value, $expire = null)
|
||||
{
|
||||
if (!$this->has($name)) {
|
||||
$time = time();
|
||||
while ($time + 5 > time() && $this->has($name . '_lock')) {
|
||||
// 存在锁定则等待
|
||||
usleep(200000);
|
||||
}
|
||||
|
||||
try {
|
||||
// 锁定
|
||||
$this->set($name . '_lock', true);
|
||||
|
||||
if ($value instanceof \Closure) {
|
||||
// 获取缓存数据
|
||||
$value = Container::getInstance()->invokeFunction($value);
|
||||
}
|
||||
|
||||
// 缓存数据
|
||||
$this->set($name, $value, $expire);
|
||||
|
||||
// 解锁
|
||||
$this->rm($name . '_lock');
|
||||
} catch (\Exception $e) {
|
||||
$this->rm($name . '_lock');
|
||||
throw $e;
|
||||
} catch (\throwable $e) {
|
||||
$this->rm($name . '_lock');
|
||||
throw $e;
|
||||
}
|
||||
} else {
|
||||
$value = $this->get($name);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
|
||||
} elseif (is_null($keys)) {
|
||||
$this->tag = $name;
|
||||
} else {
|
||||
$key = $this->getTagkey($name);
|
||||
|
||||
if (is_string($keys)) {
|
||||
$keys = explode(',', $keys);
|
||||
}
|
||||
|
||||
$keys = array_map([$this, 'getCacheKey'], $keys);
|
||||
|
||||
if ($overlay) {
|
||||
$value = $keys;
|
||||
} else {
|
||||
$value = array_unique(array_merge($this->getTagItem($name), $keys));
|
||||
}
|
||||
|
||||
$this->set($key, implode(',', $value), 0);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签
|
||||
* @access protected
|
||||
* @param string $name 缓存标识
|
||||
* @return void
|
||||
*/
|
||||
protected function setTagItem($name)
|
||||
{
|
||||
if ($this->tag) {
|
||||
$key = $this->getTagkey($this->tag);
|
||||
$this->tag = null;
|
||||
|
||||
if ($this->has($key)) {
|
||||
$value = explode(',', $this->get($key));
|
||||
$value[] = $name;
|
||||
|
||||
if (count($value) > 1000) {
|
||||
array_shift($value);
|
||||
}
|
||||
|
||||
$value = implode(',', array_unique($value));
|
||||
} else {
|
||||
$value = $name;
|
||||
}
|
||||
|
||||
$this->set($key, $value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签包含的缓存标识
|
||||
* @access protected
|
||||
* @param string $tag 缓存标签
|
||||
* @return array
|
||||
*/
|
||||
protected function getTagItem($tag)
|
||||
{
|
||||
$key = $this->getTagkey($tag);
|
||||
$value = $this->get($key);
|
||||
|
||||
if ($value) {
|
||||
return array_filter(explode(',', $value));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTagKey($tag)
|
||||
{
|
||||
return 'tag_' . md5($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化数据
|
||||
* @access protected
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
protected function serialize($data)
|
||||
{
|
||||
if (is_scalar($data) || !$this->options['serialize']) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$serialize = self::$serialize[0];
|
||||
|
||||
return self::$serialize[2] . $serialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化数据
|
||||
* @access protected
|
||||
* @param string $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function unserialize($data)
|
||||
{
|
||||
if ($this->options['serialize'] && 0 === strpos($data, self::$serialize[2])) {
|
||||
$unserialize = self::$serialize[1];
|
||||
|
||||
return $unserialize(substr($data, self::$serialize[3]));
|
||||
} else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册序列化机制
|
||||
* @access public
|
||||
* @param callable $serialize 序列化方法
|
||||
* @param callable $unserialize 反序列化方法
|
||||
* @param string $prefix 序列化前缀标识
|
||||
* @return $this
|
||||
*/
|
||||
public static function registerSerialize($serialize, $unserialize, $prefix = 'think_serialize:')
|
||||
{
|
||||
self::$serialize = [$serialize, $unserialize, $prefix, strlen($prefix)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回句柄对象,可执行其它高级方法
|
||||
*
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function handler()
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
public function getReadTimes()
|
||||
{
|
||||
return $this->readTimes;
|
||||
}
|
||||
|
||||
public function getWriteTimes()
|
||||
{
|
||||
return $this->writeTimes;
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array([$this->handler, $method], $args);
|
||||
}
|
||||
}
|
||||
307
Server/thinkphp/library/think/cache/driver/File.php
vendored
Normal file
307
Server/thinkphp/library/think/cache/driver/File.php
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
use think\Container;
|
||||
|
||||
/**
|
||||
* 文件类型缓存类
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class File extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'expire' => 0,
|
||||
'cache_subdir' => true,
|
||||
'prefix' => '',
|
||||
'path' => '',
|
||||
'hash_type' => 'md5',
|
||||
'data_compress' => false,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
protected $expire;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
if (empty($this->options['path'])) {
|
||||
$this->options['path'] = Container::get('app')->getRuntimePath() . 'cache' . DIRECTORY_SEPARATOR;
|
||||
} elseif (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
|
||||
$this->options['path'] .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化检查
|
||||
* @access private
|
||||
* @return boolean
|
||||
*/
|
||||
private function init()
|
||||
{
|
||||
// 创建项目缓存目录
|
||||
try {
|
||||
if (!is_dir($this->options['path']) && mkdir($this->options['path'], 0755, true)) {
|
||||
return true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得变量的存储文件名
|
||||
* @access protected
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool $auto 是否自动创建目录
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name, $auto = false)
|
||||
{
|
||||
$name = hash($this->options['hash_type'], $name);
|
||||
|
||||
if ($this->options['cache_subdir']) {
|
||||
// 使用子目录
|
||||
$name = substr($name, 0, 2) . DIRECTORY_SEPARATOR . substr($name, 2);
|
||||
}
|
||||
|
||||
if ($this->options['prefix']) {
|
||||
$name = $this->options['prefix'] . DIRECTORY_SEPARATOR . $name;
|
||||
}
|
||||
|
||||
$filename = $this->options['path'] . $name . '.php';
|
||||
$dir = dirname($filename);
|
||||
|
||||
if ($auto && !is_dir($dir)) {
|
||||
try {
|
||||
mkdir($dir, 0755, true);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return false !== $this->get($name) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$filename = $this->getCacheKey($name);
|
||||
|
||||
if (!is_file($filename)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$content = file_get_contents($filename);
|
||||
$this->expire = null;
|
||||
|
||||
if (false !== $content) {
|
||||
$expire = (int) substr($content, 8, 12);
|
||||
if (0 != $expire && time() > filemtime($filename) + $expire) {
|
||||
//缓存过期删除缓存文件
|
||||
$this->unlink($filename);
|
||||
return $default;
|
||||
}
|
||||
|
||||
$this->expire = $expire;
|
||||
$content = substr($content, 32);
|
||||
|
||||
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
||||
//启用数据压缩
|
||||
$content = gzuncompress($content);
|
||||
}
|
||||
return $this->unserialize($content);
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|\DateTime $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$filename = $this->getCacheKey($name, true);
|
||||
|
||||
if ($this->tag && !is_file($filename)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$data = $this->serialize($value);
|
||||
|
||||
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
||||
//数据压缩
|
||||
$data = gzcompress($data, 3);
|
||||
}
|
||||
|
||||
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
|
||||
$result = file_put_contents($filename, $data);
|
||||
|
||||
if ($result) {
|
||||
isset($first) && $this->setTagItem($filename);
|
||||
clearstatcache();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) + $step;
|
||||
$expire = $this->expire;
|
||||
} else {
|
||||
$value = $step;
|
||||
$expire = 0;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, $expire) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) - $step;
|
||||
$expire = $this->expire;
|
||||
} else {
|
||||
$value = -$step;
|
||||
$expire = 0;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, $expire) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
try {
|
||||
return $this->unlink($this->getCacheKey($name));
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
$this->unlink($key);
|
||||
}
|
||||
$this->rm($this->getTagKey($tag));
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
$files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*');
|
||||
|
||||
foreach ($files as $path) {
|
||||
if (is_dir($path)) {
|
||||
$matches = glob($path . DIRECTORY_SEPARATOR . '*.php');
|
||||
if (is_array($matches)) {
|
||||
array_map(function ($v) {
|
||||
$this->unlink($v);
|
||||
}, $matches);
|
||||
}
|
||||
rmdir($path);
|
||||
} else {
|
||||
$this->unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件是否存在后,删除
|
||||
* @access private
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @author byron sampson <xiaobo.sun@qq.com>
|
||||
* @return boolean
|
||||
*/
|
||||
private function unlink($path)
|
||||
{
|
||||
return is_file($path) && unlink($path);
|
||||
}
|
||||
|
||||
}
|
||||
209
Server/thinkphp/library/think/cache/driver/Lite.php
vendored
Normal file
209
Server/thinkphp/library/think/cache/driver/Lite.php
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* 文件类型缓存类
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Lite extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'path' => '',
|
||||
'expire' => 0, // 等于 10*365*24*3600(10年)
|
||||
];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
if (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
|
||||
$this->options['path'] .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得变量的存储文件名
|
||||
* @access protected
|
||||
* @param string $name 缓存变量名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
return $this->options['path'] . $this->options['prefix'] . md5($name) . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return $this->get($name) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$filename = $this->getCacheKey($name);
|
||||
|
||||
if (is_file($filename)) {
|
||||
// 判断是否过期
|
||||
$mtime = filemtime($filename);
|
||||
|
||||
if ($mtime < time()) {
|
||||
// 清除已经过期的文件
|
||||
unlink($filename);
|
||||
return $default;
|
||||
}
|
||||
|
||||
return include $filename;
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|\DateTime $expire 有效时间 0为永久
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp();
|
||||
} else {
|
||||
$expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire;
|
||||
$expire = time() + $expire;
|
||||
}
|
||||
|
||||
$filename = $this->getCacheKey($name);
|
||||
|
||||
if ($this->tag && !is_file($filename)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$ret = file_put_contents($filename, ("<?php return " . var_export($value, true) . ";"));
|
||||
|
||||
// 通过设置修改时间实现有效期
|
||||
if ($ret) {
|
||||
isset($first) && $this->setTagItem($filename);
|
||||
touch($filename, $expire);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) + $step;
|
||||
} else {
|
||||
$value = $step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) - $step;
|
||||
} else {
|
||||
$value = -$step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
return unlink($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
unlink($key);
|
||||
}
|
||||
|
||||
$this->rm($this->getTagKey($tag));
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*.php'));
|
||||
}
|
||||
}
|
||||
206
Server/thinkphp/library/think/cache/driver/Memcache.php
vendored
Normal file
206
Server/thinkphp/library/think/cache/driver/Memcache.php
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
class Memcache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'expire' => 0,
|
||||
'timeout' => 0, // 超时时间(单位:毫秒)
|
||||
'persistent' => true,
|
||||
'prefix' => '',
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('memcache')) {
|
||||
throw new \BadFunctionCallException('not support: memcache');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
$this->handler = new \Memcache;
|
||||
|
||||
// 支持集群
|
||||
$hosts = explode(',', $this->options['host']);
|
||||
$ports = explode(',', $this->options['port']);
|
||||
|
||||
if (empty($ports[0])) {
|
||||
$ports[0] = 11211;
|
||||
}
|
||||
|
||||
// 建立连接
|
||||
foreach ((array) $hosts as $i => $host) {
|
||||
$port = isset($ports[$i]) ? $ports[$i] : $ports[0];
|
||||
$this->options['timeout'] > 0 ?
|
||||
$this->handler->addServer($host, $port, $this->options['persistent'], 1, $this->options['timeout']) :
|
||||
$this->handler->addServer($host, $port, $this->options['persistent'], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return false !== $this->handler->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$result = $this->handler->get($this->getCacheKey($name));
|
||||
|
||||
return false !== $result ? $this->unserialize($result) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|DateTime $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($this->handler->set($key, $value, 0, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
if ($this->handler->get($key)) {
|
||||
return $this->handler->increment($key, $step);
|
||||
}
|
||||
|
||||
return $this->handler->set($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$value = $this->handler->get($key) - $step;
|
||||
$res = $this->handler->set($key, $value);
|
||||
|
||||
return !$res ? false : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @return bool
|
||||
*/
|
||||
public function rm($name, $ttl = false)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return false === $ttl ?
|
||||
$this->handler->delete($key) :
|
||||
$this->handler->delete($key, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->delete($key);
|
||||
}
|
||||
|
||||
$tagName = $this->getTagKey($tag);
|
||||
$this->rm($tagName);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->flush();
|
||||
}
|
||||
|
||||
}
|
||||
279
Server/thinkphp/library/think/cache/driver/Memcached.php
vendored
Normal file
279
Server/thinkphp/library/think/cache/driver/Memcached.php
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
class Memcached extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'expire' => 0,
|
||||
'timeout' => 0, // 超时时间(单位:毫秒)
|
||||
'prefix' => '',
|
||||
'username' => '', //账号
|
||||
'password' => '', //密码
|
||||
'option' => [],
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('memcached')) {
|
||||
throw new \BadFunctionCallException('not support: memcached');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
$this->handler = new \Memcached;
|
||||
|
||||
if (!empty($this->options['option'])) {
|
||||
$this->handler->setOptions($this->options['option']);
|
||||
}
|
||||
|
||||
// 设置连接超时时间(单位:毫秒)
|
||||
if ($this->options['timeout'] > 0) {
|
||||
$this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']);
|
||||
}
|
||||
|
||||
// 支持集群
|
||||
$hosts = explode(',', $this->options['host']);
|
||||
$ports = explode(',', $this->options['port']);
|
||||
if (empty($ports[0])) {
|
||||
$ports[0] = 11211;
|
||||
}
|
||||
|
||||
// 建立连接
|
||||
$servers = [];
|
||||
foreach ((array) $hosts as $i => $host) {
|
||||
$servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1];
|
||||
}
|
||||
|
||||
$this->handler->addServers($servers);
|
||||
$this->handler->setOption(\Memcached::OPT_COMPRESSION, false);
|
||||
if ('' != $this->options['username']) {
|
||||
$this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
||||
$this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return $this->handler->get($key) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$result = $this->handler->get($this->getCacheKey($name));
|
||||
|
||||
return false !== $result ? $this->unserialize($result) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($this->handler->set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
if ($this->handler->get($key)) {
|
||||
return $this->handler->increment($key, $step);
|
||||
}
|
||||
|
||||
return $this->handler->set($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$value = $this->handler->get($key) - $step;
|
||||
$res = $this->handler->set($key, $value);
|
||||
|
||||
return !$res ? false : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @return bool
|
||||
*/
|
||||
public function rm($name, $ttl = false)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return false === $ttl ?
|
||||
$this->handler->delete($key) :
|
||||
$this->handler->delete($key, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
$this->handler->deleteMulti($keys);
|
||||
$this->rm($this->getTagKey($tag));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
if (is_null($keys)) {
|
||||
$this->tag = $name;
|
||||
} else {
|
||||
$tagName = $this->getTagKey($name);
|
||||
if ($overlay) {
|
||||
$this->handler->delete($tagName);
|
||||
}
|
||||
|
||||
if (!$this->has($tagName)) {
|
||||
$this->handler->set($tagName, '');
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->append($tagName, ',' . $key);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签
|
||||
* @access protected
|
||||
* @param string $name 缓存标识
|
||||
* @return void
|
||||
*/
|
||||
protected function setTagItem($name)
|
||||
{
|
||||
if ($this->tag) {
|
||||
$tagName = $this->getTagKey($this->tag);
|
||||
|
||||
if ($this->has($tagName)) {
|
||||
$this->handler->append($tagName, ',' . $name);
|
||||
} else {
|
||||
$this->handler->set($tagName, $name);
|
||||
}
|
||||
|
||||
$this->tag = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签包含的缓存标识
|
||||
* @access public
|
||||
* @param string $tag 缓存标签
|
||||
* @return array
|
||||
*/
|
||||
public function getTagItem($tag)
|
||||
{
|
||||
$tagName = $this->getTagKey($tag);
|
||||
return explode(',', trim($this->handler->get($tagName), ','));
|
||||
}
|
||||
}
|
||||
272
Server/thinkphp/library/think/cache/driver/Redis.php
vendored
Normal file
272
Server/thinkphp/library/think/cache/driver/Redis.php
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Redis缓存驱动,适合单机部署、有前端代理实现高可用的场景,性能最好
|
||||
* 有需要在业务层实现读写分离、或者使用RedisCluster的需求,请使用Redisd驱动
|
||||
*
|
||||
* 要求安装phpredis扩展:https://github.com/nicolasff/phpredis
|
||||
* @author 尘缘 <130775@qq.com>
|
||||
*/
|
||||
class Redis extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'password' => '',
|
||||
'select' => 0,
|
||||
'timeout' => 0,
|
||||
'expire' => 0,
|
||||
'persistent' => false,
|
||||
'prefix' => '',
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
if (extension_loaded('redis')) {
|
||||
$this->handler = new \Redis;
|
||||
|
||||
if ($this->options['persistent']) {
|
||||
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
|
||||
} else {
|
||||
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
|
||||
}
|
||||
|
||||
if ('' != $this->options['password']) {
|
||||
$this->handler->auth($this->options['password']);
|
||||
}
|
||||
|
||||
if (0 != $this->options['select']) {
|
||||
$this->handler->select($this->options['select']);
|
||||
}
|
||||
} elseif (class_exists('\Predis\Client')) {
|
||||
$params = [];
|
||||
foreach ($this->options as $key => $val) {
|
||||
if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication', 'parameters'])) {
|
||||
$params[$key] = $val;
|
||||
unset($this->options[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if ('' == $this->options['password']) {
|
||||
unset($this->options['password']);
|
||||
}
|
||||
|
||||
$this->handler = new \Predis\Client($this->options, $params);
|
||||
|
||||
$this->options['prefix'] = '';
|
||||
} else {
|
||||
throw new \BadFunctionCallException('not support: redis');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return $this->handler->exists($this->getCacheKey($name)) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$value = $this->handler->get($this->getCacheKey($name));
|
||||
|
||||
if (is_null($value) || false === $value) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->unserialize($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($expire) {
|
||||
$result = $this->handler->setex($key, $expire, $value);
|
||||
} else {
|
||||
$result = $this->handler->set($key, $value);
|
||||
}
|
||||
|
||||
isset($first) && $this->setTagItem($key);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return $this->handler->incrby($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return $this->handler->decrby($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->del($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
$this->handler->del($keys);
|
||||
|
||||
$tagName = $this->getTagKey($tag);
|
||||
$this->handler->del($tagName);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->flushDB();
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
if (is_null($keys)) {
|
||||
$this->tag = $name;
|
||||
} else {
|
||||
$tagName = $this->getTagKey($name);
|
||||
if ($overlay) {
|
||||
$this->handler->del($tagName);
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->sAdd($tagName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签
|
||||
* @access protected
|
||||
* @param string $name 缓存标识
|
||||
* @return void
|
||||
*/
|
||||
protected function setTagItem($name)
|
||||
{
|
||||
if ($this->tag) {
|
||||
$tagName = $this->getTagKey($this->tag);
|
||||
$this->handler->sAdd($tagName, $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签包含的缓存标识
|
||||
* @access protected
|
||||
* @param string $tag 缓存标签
|
||||
* @return array
|
||||
*/
|
||||
protected function getTagItem($tag)
|
||||
{
|
||||
$tagName = $this->getTagKey($tag);
|
||||
return $this->handler->sMembers($tagName);
|
||||
}
|
||||
}
|
||||
233
Server/thinkphp/library/think/cache/driver/Sqlite.php
vendored
Normal file
233
Server/thinkphp/library/think/cache/driver/Sqlite.php
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Sqlite缓存驱动
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Sqlite extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'db' => ':memory:',
|
||||
'table' => 'sharedmemory',
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'persistent' => false,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('sqlite')) {
|
||||
throw new \BadFunctionCallException('not support: sqlite');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
$func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open';
|
||||
|
||||
$this->handler = $func($this->options['db']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的缓存标识
|
||||
* @access public
|
||||
* @param string $name 缓存名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
return $this->options['prefix'] . sqlite_escape_string($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1';
|
||||
$result = sqlite_query($this->handler, $sql);
|
||||
|
||||
return sqlite_num_rows($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$name = $this->getCacheKey($name);
|
||||
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1';
|
||||
|
||||
$result = sqlite_query($this->handler, $sql);
|
||||
|
||||
if (sqlite_num_rows($result)) {
|
||||
$content = sqlite_fetch_single($result);
|
||||
if (function_exists('gzcompress')) {
|
||||
//启用数据压缩
|
||||
$content = gzuncompress($content);
|
||||
}
|
||||
|
||||
return $this->unserialize($content);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$name = $this->getCacheKey($name);
|
||||
|
||||
$value = sqlite_escape_string($this->serialize($value));
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp();
|
||||
} else {
|
||||
$expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存
|
||||
}
|
||||
|
||||
if (function_exists('gzcompress')) {
|
||||
//数据压缩
|
||||
$value = gzcompress($value, 3);
|
||||
}
|
||||
|
||||
if ($this->tag) {
|
||||
$tag = $this->tag;
|
||||
$this->tag = null;
|
||||
} else {
|
||||
$tag = '';
|
||||
}
|
||||
|
||||
$sql = 'REPLACE INTO ' . $this->options['table'] . ' (var, value, expire, tag) VALUES (\'' . $name . '\', \'' . $value . '\', \'' . $expire . '\', \'' . $tag . '\')';
|
||||
|
||||
if (sqlite_query($this->handler, $sql)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) + $step;
|
||||
} else {
|
||||
$value = $step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) - $step;
|
||||
} else {
|
||||
$value = -$step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$name = $this->getCacheKey($name);
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\'';
|
||||
sqlite_query($this->handler, $sql);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
$name = sqlite_escape_string($this->getTagKey($tag));
|
||||
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\'';
|
||||
sqlite_query($this->handler, $sql);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->options['table'];
|
||||
|
||||
sqlite_query($this->handler, $sql);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
175
Server/thinkphp/library/think/cache/driver/Wincache.php
vendored
Normal file
175
Server/thinkphp/library/think/cache/driver/Wincache.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Wincache缓存驱动
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Wincache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!function_exists('wincache_ucache_info')) {
|
||||
throw new \BadFunctionCallException('not support: WinCache');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return wincache_ucache_exists($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return wincache_ucache_exists($key) ? $this->unserialize(wincache_ucache_get($key)) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
if (wincache_ucache_set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return wincache_ucache_inc($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return wincache_ucache_dec($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
return wincache_ucache_delete($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
wincache_ucache_delete($keys);
|
||||
|
||||
$tagName = $this->getTagkey($tag);
|
||||
$this->rm($tagName);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
return wincache_ucache_clear();
|
||||
}
|
||||
|
||||
}
|
||||
179
Server/thinkphp/library/think/cache/driver/Xcache.php
vendored
Normal file
179
Server/thinkphp/library/think/cache/driver/Xcache.php
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Xcache缓存驱动
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Xcache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!function_exists('xcache_info')) {
|
||||
throw new \BadFunctionCallException('not support: Xcache');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return xcache_isset($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return xcache_isset($key) ? $this->unserialize(xcache_get($key)) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if (xcache_set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return xcache_inc($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return xcache_dec($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
return xcache_unset($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
xcache_unset($key);
|
||||
}
|
||||
|
||||
$this->rm($this->getTagKey($tag));
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
if (function_exists('xcache_unset_by_prefix')) {
|
||||
return xcache_unset_by_prefix($this->options['prefix']);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Server/thinkphp/library/think/config/driver/Ini.php
Normal file
31
Server/thinkphp/library/think/config/driver/Ini.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\config\driver;
|
||||
|
||||
class Ini
|
||||
{
|
||||
protected $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function parse()
|
||||
{
|
||||
if (is_file($this->config)) {
|
||||
return parse_ini_file($this->config, true);
|
||||
} else {
|
||||
return parse_ini_string($this->config, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Server/thinkphp/library/think/config/driver/Json.php
Normal file
31
Server/thinkphp/library/think/config/driver/Json.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\config\driver;
|
||||
|
||||
class Json
|
||||
{
|
||||
protected $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
if (is_file($config)) {
|
||||
$config = file_get_contents($config);
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function parse()
|
||||
{
|
||||
return json_decode($this->config, true);
|
||||
}
|
||||
}
|
||||
40
Server/thinkphp/library/think/config/driver/Xml.php
Normal file
40
Server/thinkphp/library/think/config/driver/Xml.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\config\driver;
|
||||
|
||||
class Xml
|
||||
{
|
||||
protected $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function parse()
|
||||
{
|
||||
if (is_file($this->config)) {
|
||||
$content = simplexml_load_file($this->config);
|
||||
} else {
|
||||
$content = simplexml_load_string($this->config);
|
||||
}
|
||||
|
||||
$result = (array) $content;
|
||||
foreach ($result as $key => $val) {
|
||||
if (is_object($val)) {
|
||||
$result[$key] = (array) $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
482
Server/thinkphp/library/think/console/Command.php
Normal file
482
Server/thinkphp/library/think/console/Command.php
Normal file
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console;
|
||||
|
||||
use think\Console;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Definition;
|
||||
use think\console\input\Option;
|
||||
|
||||
class Command
|
||||
{
|
||||
|
||||
/** @var Console */
|
||||
private $console;
|
||||
private $name;
|
||||
private $aliases = [];
|
||||
private $definition;
|
||||
private $help;
|
||||
private $description;
|
||||
private $ignoreValidationErrors = false;
|
||||
private $consoleDefinitionMerged = false;
|
||||
private $consoleDefinitionMergedWithArgs = false;
|
||||
private $code;
|
||||
private $synopsis = [];
|
||||
private $usages = [];
|
||||
|
||||
/** @var Input */
|
||||
protected $input;
|
||||
|
||||
/** @var Output */
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string|null $name 命令名称,如果没有设置则比如在 configure() 里设置
|
||||
* @throws \LogicException
|
||||
* @api
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
$this->definition = new Definition();
|
||||
|
||||
if (null !== $name) {
|
||||
$this->setName($name);
|
||||
}
|
||||
|
||||
$this->configure();
|
||||
|
||||
if (!$this->name) {
|
||||
throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略验证错误
|
||||
*/
|
||||
public function ignoreValidationErrors()
|
||||
{
|
||||
$this->ignoreValidationErrors = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置控制台
|
||||
* @param Console $console
|
||||
*/
|
||||
public function setConsole(Console $console = null)
|
||||
{
|
||||
$this->console = $console;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取控制台
|
||||
* @return Console
|
||||
* @api
|
||||
*/
|
||||
public function getConsole()
|
||||
{
|
||||
return $this->console;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有效
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置指令
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return null|int
|
||||
* @throws \LogicException
|
||||
* @see setCode()
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
throw new \LogicException('You must override the execute() method in the concrete command class.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户验证
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
*/
|
||||
protected function interact(Input $input, Output $output)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param Input $input An InputInterface instance
|
||||
* @param Output $output An OutputInterface instance
|
||||
*/
|
||||
protected function initialize(Input $input, Output $output)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
* @see setCode()
|
||||
* @see execute()
|
||||
*/
|
||||
public function run(Input $input, Output $output)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
$this->getSynopsis(true);
|
||||
$this->getSynopsis(false);
|
||||
|
||||
$this->mergeConsoleDefinition();
|
||||
|
||||
try {
|
||||
$input->bind($this->definition);
|
||||
} catch (\Exception $e) {
|
||||
if (!$this->ignoreValidationErrors) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initialize($input, $output);
|
||||
|
||||
if ($input->isInteractive()) {
|
||||
$this->interact($input, $output);
|
||||
}
|
||||
|
||||
$input->validate();
|
||||
|
||||
if ($this->code) {
|
||||
$statusCode = call_user_func($this->code, $input, $output);
|
||||
} else {
|
||||
$statusCode = $this->execute($input, $output);
|
||||
}
|
||||
|
||||
return is_numeric($statusCode) ? (int) $statusCode : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置执行代码
|
||||
* @param callable $code callable(InputInterface $input, OutputInterface $output)
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
* @see execute()
|
||||
*/
|
||||
public function setCode(callable $code)
|
||||
{
|
||||
if (!is_callable($code)) {
|
||||
throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) {
|
||||
$r = new \ReflectionFunction($code);
|
||||
if (null === $r->getClosureThis()) {
|
||||
$code = \Closure::bind($code, $this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并参数定义
|
||||
* @param bool $mergeArgs
|
||||
*/
|
||||
public function mergeConsoleDefinition($mergeArgs = true)
|
||||
{
|
||||
if (null === $this->console
|
||||
|| (true === $this->consoleDefinitionMerged
|
||||
&& ($this->consoleDefinitionMergedWithArgs || !$mergeArgs))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($mergeArgs) {
|
||||
$currentArguments = $this->definition->getArguments();
|
||||
$this->definition->setArguments($this->console->getDefinition()->getArguments());
|
||||
$this->definition->addArguments($currentArguments);
|
||||
}
|
||||
|
||||
$this->definition->addOptions($this->console->getDefinition()->getOptions());
|
||||
|
||||
$this->consoleDefinitionMerged = true;
|
||||
if ($mergeArgs) {
|
||||
$this->consoleDefinitionMergedWithArgs = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置参数定义
|
||||
* @param array|Definition $definition
|
||||
* @return Command
|
||||
* @api
|
||||
*/
|
||||
public function setDefinition($definition)
|
||||
{
|
||||
if ($definition instanceof Definition) {
|
||||
$this->definition = $definition;
|
||||
} else {
|
||||
$this->definition->setDefinition($definition);
|
||||
}
|
||||
|
||||
$this->consoleDefinitionMerged = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数定义
|
||||
* @return Definition
|
||||
* @api
|
||||
*/
|
||||
public function getDefinition()
|
||||
{
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前指令的参数定义
|
||||
* @return Definition
|
||||
*/
|
||||
public function getNativeDefinition()
|
||||
{
|
||||
return $this->getDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数
|
||||
* @param string $name 名称
|
||||
* @param int $mode 类型
|
||||
* @param string $description 描述
|
||||
* @param mixed $default 默认值
|
||||
* @return Command
|
||||
*/
|
||||
public function addArgument($name, $mode = null, $description = '', $default = null)
|
||||
{
|
||||
$this->definition->addArgument(new Argument($name, $mode, $description, $default));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加选项
|
||||
* @param string $name 选项名称
|
||||
* @param string $shortcut 别名
|
||||
* @param int $mode 类型
|
||||
* @param string $description 描述
|
||||
* @param mixed $default 默认值
|
||||
* @return Command
|
||||
*/
|
||||
public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
|
||||
{
|
||||
$this->definition->addOption(new Option($name, $shortcut, $mode, $description, $default));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指令名称
|
||||
* @param string $name
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->validateName($name);
|
||||
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令名称
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置描述
|
||||
* @param string $description
|
||||
* @return Command
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取描述
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置帮助信息
|
||||
* @param string $help
|
||||
* @return Command
|
||||
*/
|
||||
public function setHelp($help)
|
||||
{
|
||||
$this->help = $help;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取帮助信息
|
||||
* @return string
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
return $this->help;
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述信息
|
||||
* @return string
|
||||
*/
|
||||
public function getProcessedHelp()
|
||||
{
|
||||
$name = $this->name;
|
||||
|
||||
$placeholders = [
|
||||
'%command.name%',
|
||||
'%command.full_name%',
|
||||
];
|
||||
$replacements = [
|
||||
$name,
|
||||
$_SERVER['PHP_SELF'] . ' ' . $name,
|
||||
];
|
||||
|
||||
return str_replace($placeholders, $replacements, $this->getHelp());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置别名
|
||||
* @param string[] $aliases
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setAliases($aliases)
|
||||
{
|
||||
if (!is_array($aliases) && !$aliases instanceof \Traversable) {
|
||||
throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
|
||||
}
|
||||
|
||||
foreach ($aliases as $alias) {
|
||||
$this->validateName($alias);
|
||||
}
|
||||
|
||||
$this->aliases = $aliases;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取别名
|
||||
* @return array
|
||||
*/
|
||||
public function getAliases()
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取简介
|
||||
* @param bool $short 是否简单的
|
||||
* @return string
|
||||
*/
|
||||
public function getSynopsis($short = false)
|
||||
{
|
||||
$key = $short ? 'short' : 'long';
|
||||
|
||||
if (!isset($this->synopsis[$key])) {
|
||||
$this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
|
||||
}
|
||||
|
||||
return $this->synopsis[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用法介绍
|
||||
* @param string $usage
|
||||
* @return $this
|
||||
*/
|
||||
public function addUsage($usage)
|
||||
{
|
||||
if (0 !== strpos($usage, $this->name)) {
|
||||
$usage = sprintf('%s %s', $this->name, $usage);
|
||||
}
|
||||
|
||||
$this->usages[] = $usage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用法介绍
|
||||
* @return array
|
||||
*/
|
||||
public function getUsages()
|
||||
{
|
||||
return $this->usages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证指令名称
|
||||
* @param string $name
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function validateName($name)
|
||||
{
|
||||
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
|
||||
throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出表格
|
||||
* @param Table $table
|
||||
* @return string
|
||||
*/
|
||||
protected function table(Table $table)
|
||||
{
|
||||
$content = $table->render();
|
||||
$this->output->writeln($content);
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
464
Server/thinkphp/library/think/console/Input.php
Normal file
464
Server/thinkphp/library/think/console/Input.php
Normal file
@@ -0,0 +1,464 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console;
|
||||
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Definition;
|
||||
use think\console\input\Option;
|
||||
|
||||
class Input
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Definition
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* @var Option[]
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @var Argument[]
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
protected $interactive = true;
|
||||
|
||||
private $tokens;
|
||||
private $parsed;
|
||||
|
||||
public function __construct($argv = null)
|
||||
{
|
||||
if (null === $argv) {
|
||||
$argv = $_SERVER['argv'];
|
||||
// 去除命令名
|
||||
array_shift($argv);
|
||||
}
|
||||
|
||||
$this->tokens = $argv;
|
||||
|
||||
$this->definition = new Definition();
|
||||
}
|
||||
|
||||
protected function setTokens(array $tokens)
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定实例
|
||||
* @param Definition $definition A InputDefinition instance
|
||||
*/
|
||||
public function bind(Definition $definition)
|
||||
{
|
||||
$this->arguments = [];
|
||||
$this->options = [];
|
||||
$this->definition = $definition;
|
||||
|
||||
$this->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析参数
|
||||
*/
|
||||
protected function parse()
|
||||
{
|
||||
$parseOptions = true;
|
||||
$this->parsed = $this->tokens;
|
||||
while (null !== $token = array_shift($this->parsed)) {
|
||||
if ($parseOptions && '' == $token) {
|
||||
$this->parseArgument($token);
|
||||
} elseif ($parseOptions && '--' == $token) {
|
||||
$parseOptions = false;
|
||||
} elseif ($parseOptions && 0 === strpos($token, '--')) {
|
||||
$this->parseLongOption($token);
|
||||
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
|
||||
$this->parseShortOption($token);
|
||||
} else {
|
||||
$this->parseArgument($token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析短选项
|
||||
* @param string $token 当前的指令.
|
||||
*/
|
||||
private function parseShortOption($token)
|
||||
{
|
||||
$name = substr($token, 1);
|
||||
|
||||
if (strlen($name) > 1) {
|
||||
if ($this->definition->hasShortcut($name[0])
|
||||
&& $this->definition->getOptionForShortcut($name[0])->acceptValue()
|
||||
) {
|
||||
$this->addShortOption($name[0], substr($name, 1));
|
||||
} else {
|
||||
$this->parseShortOptionSet($name);
|
||||
}
|
||||
} else {
|
||||
$this->addShortOption($name, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析短选项
|
||||
* @param string $name 当前指令
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function parseShortOptionSet($name)
|
||||
{
|
||||
$len = strlen($name);
|
||||
for ($i = 0; $i < $len; ++$i) {
|
||||
if (!$this->definition->hasShortcut($name[$i])) {
|
||||
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
|
||||
}
|
||||
|
||||
$option = $this->definition->getOptionForShortcut($name[$i]);
|
||||
if ($option->acceptValue()) {
|
||||
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
|
||||
|
||||
break;
|
||||
} else {
|
||||
$this->addLongOption($option->getName(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析完整选项
|
||||
* @param string $token 当前指令
|
||||
*/
|
||||
private function parseLongOption($token)
|
||||
{
|
||||
$name = substr($token, 2);
|
||||
|
||||
if (false !== $pos = strpos($name, '=')) {
|
||||
$this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
|
||||
} else {
|
||||
$this->addLongOption($name, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析参数
|
||||
* @param string $token 当前指令
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function parseArgument($token)
|
||||
{
|
||||
$c = count($this->arguments);
|
||||
|
||||
if ($this->definition->hasArgument($c)) {
|
||||
$arg = $this->definition->getArgument($c);
|
||||
|
||||
$this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
|
||||
|
||||
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
|
||||
$arg = $this->definition->getArgument($c - 1);
|
||||
|
||||
$this->arguments[$arg->getName()][] = $token;
|
||||
} else {
|
||||
throw new \RuntimeException('Too many arguments.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个短选项的值
|
||||
* @param string $shortcut 短名称
|
||||
* @param mixed $value 值
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function addShortOption($shortcut, $value)
|
||||
{
|
||||
if (!$this->definition->hasShortcut($shortcut)) {
|
||||
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
|
||||
}
|
||||
|
||||
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个完整选项的值
|
||||
* @param string $name 选项名
|
||||
* @param mixed $value 值
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function addLongOption($name, $value)
|
||||
{
|
||||
if (!$this->definition->hasOption($name)) {
|
||||
throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
|
||||
}
|
||||
|
||||
$option = $this->definition->getOption($name);
|
||||
|
||||
if (false === $value) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if (null !== $value && !$option->acceptValue()) {
|
||||
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
|
||||
}
|
||||
|
||||
if (null === $value && $option->acceptValue() && count($this->parsed)) {
|
||||
$next = array_shift($this->parsed);
|
||||
if (isset($next[0]) && '-' !== $next[0]) {
|
||||
$value = $next;
|
||||
} elseif (empty($next)) {
|
||||
$value = '';
|
||||
} else {
|
||||
array_unshift($this->parsed, $next);
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
if ($option->isValueRequired()) {
|
||||
throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
|
||||
}
|
||||
|
||||
if (!$option->isArray()) {
|
||||
$value = $option->isValueOptional() ? $option->getDefault() : true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($option->isArray()) {
|
||||
$this->options[$name][] = $value;
|
||||
} else {
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第一个参数
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFirstArgument()
|
||||
{
|
||||
foreach ($this->tokens as $token) {
|
||||
if ($token && '-' === $token[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查原始参数是否包含某个值
|
||||
* @param string|array $values 需要检查的值
|
||||
* @return bool
|
||||
*/
|
||||
public function hasParameterOption($values)
|
||||
{
|
||||
$values = (array) $values;
|
||||
|
||||
foreach ($this->tokens as $token) {
|
||||
foreach ($values as $value) {
|
||||
if ($token === $value || 0 === strpos($token, $value . '=')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始选项的值
|
||||
* @param string|array $values 需要检查的值
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed The option value
|
||||
*/
|
||||
public function getParameterOption($values, $default = false)
|
||||
{
|
||||
$values = (array) $values;
|
||||
$tokens = $this->tokens;
|
||||
|
||||
while (0 < count($tokens)) {
|
||||
$token = array_shift($tokens);
|
||||
|
||||
foreach ($values as $value) {
|
||||
if ($token === $value || 0 === strpos($token, $value . '=')) {
|
||||
if (false !== $pos = strpos($token, '=')) {
|
||||
return substr($token, $pos + 1);
|
||||
}
|
||||
|
||||
return array_shift($tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证输入
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
|
||||
throw new \RuntimeException('Not enough arguments.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查输入是否是交互的
|
||||
* @return bool
|
||||
*/
|
||||
public function isInteractive()
|
||||
{
|
||||
return $this->interactive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入的交互
|
||||
* @param bool
|
||||
*/
|
||||
public function setInteractive($interactive)
|
||||
{
|
||||
$this->interactive = (bool) $interactive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的参数
|
||||
* @return Argument[]
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取参数
|
||||
* @param string $name 参数名
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getArgument($name)
|
||||
{
|
||||
if (!$this->definition->hasArgument($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
|
||||
}
|
||||
|
||||
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)
|
||||
->getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置参数的值
|
||||
* @param string $name 参数名
|
||||
* @param string $value 值
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setArgument($name, $value)
|
||||
{
|
||||
if (!$this->definition->hasArgument($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
|
||||
}
|
||||
|
||||
$this->arguments[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在某个参数
|
||||
* @param string|int $name 参数名或位置
|
||||
* @return bool
|
||||
*/
|
||||
public function hasArgument($name)
|
||||
{
|
||||
return $this->definition->hasArgument($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的选项
|
||||
* @return Option[]
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return array_merge($this->definition->getOptionDefaults(), $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选项值
|
||||
* @param string $name 选项名称
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getOption($name)
|
||||
{
|
||||
if (!$this->definition->hasOption($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
|
||||
}
|
||||
|
||||
return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选项值
|
||||
* @param string $name 选项名
|
||||
* @param string|bool $value 值
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
if (!$this->definition->hasOption($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
|
||||
}
|
||||
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有某个选项
|
||||
* @param string $name 选项名
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOption($name)
|
||||
{
|
||||
return $this->definition->hasOption($name) && isset($this->options[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义指令
|
||||
* @param string $token
|
||||
* @return string
|
||||
*/
|
||||
public function escapeToken($token)
|
||||
{
|
||||
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回传递给命令的参数的字符串
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$tokens = array_map(function ($token) {
|
||||
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
|
||||
return $match[1] . $this->escapeToken($match[2]);
|
||||
}
|
||||
|
||||
if ($token && '-' !== $token[0]) {
|
||||
return $this->escapeToken($token);
|
||||
}
|
||||
|
||||
return $token;
|
||||
}, $this->tokens);
|
||||
|
||||
return implode(' ', $tokens);
|
||||
}
|
||||
}
|
||||
19
Server/thinkphp/library/think/console/LICENSE
Normal file
19
Server/thinkphp/library/think/console/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-2016 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
222
Server/thinkphp/library/think/console/Output.php
Normal file
222
Server/thinkphp/library/think/console/Output.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console;
|
||||
|
||||
use Exception;
|
||||
use think\console\output\Ask;
|
||||
use think\console\output\Descriptor;
|
||||
use think\console\output\driver\Buffer;
|
||||
use think\console\output\driver\Console;
|
||||
use think\console\output\driver\Nothing;
|
||||
use think\console\output\Question;
|
||||
use think\console\output\question\Choice;
|
||||
use think\console\output\question\Confirmation;
|
||||
|
||||
/**
|
||||
* Class Output
|
||||
* @package think\console
|
||||
*
|
||||
* @see \think\console\output\driver\Console::setDecorated
|
||||
* @method void setDecorated($decorated)
|
||||
*
|
||||
* @see \think\console\output\driver\Buffer::fetch
|
||||
* @method string fetch()
|
||||
*
|
||||
* @method void info($message)
|
||||
* @method void error($message)
|
||||
* @method void comment($message)
|
||||
* @method void warning($message)
|
||||
* @method void highlight($message)
|
||||
* @method void question($message)
|
||||
*/
|
||||
class Output
|
||||
{
|
||||
const VERBOSITY_QUIET = 0;
|
||||
const VERBOSITY_NORMAL = 1;
|
||||
const VERBOSITY_VERBOSE = 2;
|
||||
const VERBOSITY_VERY_VERBOSE = 3;
|
||||
const VERBOSITY_DEBUG = 4;
|
||||
|
||||
const OUTPUT_NORMAL = 0;
|
||||
const OUTPUT_RAW = 1;
|
||||
const OUTPUT_PLAIN = 2;
|
||||
|
||||
private $verbosity = self::VERBOSITY_NORMAL;
|
||||
|
||||
/** @var Buffer|Console|Nothing */
|
||||
private $handle = null;
|
||||
|
||||
protected $styles = [
|
||||
'info',
|
||||
'error',
|
||||
'comment',
|
||||
'question',
|
||||
'highlight',
|
||||
'warning'
|
||||
];
|
||||
|
||||
public function __construct($driver = 'console')
|
||||
{
|
||||
$class = '\\think\\console\\output\\driver\\' . ucwords($driver);
|
||||
|
||||
$this->handle = new $class($this);
|
||||
}
|
||||
|
||||
public function ask(Input $input, $question, $default = null, $validator = null)
|
||||
{
|
||||
$question = new Question($question, $default);
|
||||
$question->setValidator($validator);
|
||||
|
||||
return $this->askQuestion($input, $question);
|
||||
}
|
||||
|
||||
public function askHidden(Input $input, $question, $validator = null)
|
||||
{
|
||||
$question = new Question($question);
|
||||
|
||||
$question->setHidden(true);
|
||||
$question->setValidator($validator);
|
||||
|
||||
return $this->askQuestion($input, $question);
|
||||
}
|
||||
|
||||
public function confirm(Input $input, $question, $default = true)
|
||||
{
|
||||
return $this->askQuestion($input, new Confirmation($question, $default));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function choice(Input $input, $question, array $choices, $default = null)
|
||||
{
|
||||
if (null !== $default) {
|
||||
$values = array_flip($choices);
|
||||
$default = $values[$default];
|
||||
}
|
||||
|
||||
return $this->askQuestion($input, new Choice($question, $choices, $default));
|
||||
}
|
||||
|
||||
protected function askQuestion(Input $input, Question $question)
|
||||
{
|
||||
$ask = new Ask($input, $this, $question);
|
||||
$answer = $ask->run();
|
||||
|
||||
if ($input->isInteractive()) {
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
return $answer;
|
||||
}
|
||||
|
||||
protected function block($style, $message)
|
||||
{
|
||||
$this->writeln("<{$style}>{$message}</$style>");
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出空行
|
||||
* @param int $count
|
||||
*/
|
||||
public function newLine($count = 1)
|
||||
{
|
||||
$this->write(str_repeat(PHP_EOL, $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出信息并换行
|
||||
* @param string $messages
|
||||
* @param int $type
|
||||
*/
|
||||
public function writeln($messages, $type = self::OUTPUT_NORMAL)
|
||||
{
|
||||
$this->write($messages, true, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出信息
|
||||
* @param string $messages
|
||||
* @param bool $newline
|
||||
* @param int $type
|
||||
*/
|
||||
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
|
||||
{
|
||||
$this->handle->write($messages, $newline, $type);
|
||||
}
|
||||
|
||||
public function renderException(\Exception $e)
|
||||
{
|
||||
$this->handle->renderException($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setVerbosity($level)
|
||||
{
|
||||
$this->verbosity = (int) $level;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVerbosity()
|
||||
{
|
||||
return $this->verbosity;
|
||||
}
|
||||
|
||||
public function isQuiet()
|
||||
{
|
||||
return self::VERBOSITY_QUIET === $this->verbosity;
|
||||
}
|
||||
|
||||
public function isVerbose()
|
||||
{
|
||||
return self::VERBOSITY_VERBOSE <= $this->verbosity;
|
||||
}
|
||||
|
||||
public function isVeryVerbose()
|
||||
{
|
||||
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
|
||||
}
|
||||
|
||||
public function isDebug()
|
||||
{
|
||||
return self::VERBOSITY_DEBUG <= $this->verbosity;
|
||||
}
|
||||
|
||||
public function describe($object, array $options = [])
|
||||
{
|
||||
$descriptor = new Descriptor();
|
||||
$options = array_merge([
|
||||
'raw_text' => false,
|
||||
], $options);
|
||||
|
||||
$descriptor->describe($this, $object, $options);
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if (in_array($method, $this->styles)) {
|
||||
array_unshift($args, $method);
|
||||
return call_user_func_array([$this, 'block'], $args);
|
||||
}
|
||||
|
||||
if ($this->handle && method_exists($this->handle, $method)) {
|
||||
return call_user_func_array([$this->handle, $method], $args);
|
||||
} else {
|
||||
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
281
Server/thinkphp/library/think/console/Table.php
Normal file
281
Server/thinkphp/library/think/console/Table.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console;
|
||||
|
||||
class Table
|
||||
{
|
||||
const ALIGN_LEFT = 1;
|
||||
const ALIGN_RIGHT = 0;
|
||||
const ALIGN_CENTER = 2;
|
||||
|
||||
/**
|
||||
* 头信息数据
|
||||
* @var array
|
||||
*/
|
||||
protected $header = [];
|
||||
|
||||
/**
|
||||
* 头部对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
|
||||
* @var int
|
||||
*/
|
||||
protected $headerAlign = 1;
|
||||
|
||||
/**
|
||||
* 表格数据(二维数组)
|
||||
* @var array
|
||||
*/
|
||||
protected $rows = [];
|
||||
|
||||
/**
|
||||
* 单元格对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
|
||||
* @var int
|
||||
*/
|
||||
protected $cellAlign = 1;
|
||||
|
||||
/**
|
||||
* 单元格宽度信息
|
||||
* @var array
|
||||
*/
|
||||
protected $colWidth = [];
|
||||
|
||||
/**
|
||||
* 表格输出样式
|
||||
* @var string
|
||||
*/
|
||||
protected $style = 'default';
|
||||
|
||||
/**
|
||||
* 表格样式定义
|
||||
* @var array
|
||||
*/
|
||||
protected $format = [
|
||||
'compact' => [],
|
||||
'default' => [
|
||||
'top' => ['+', '-', '+', '+'],
|
||||
'cell' => ['|', ' ', '|', '|'],
|
||||
'middle' => ['+', '-', '+', '+'],
|
||||
'bottom' => ['+', '-', '+', '+'],
|
||||
'cross-top' => ['+', '-', '-', '+'],
|
||||
'cross-bottom' => ['+', '-', '-', '+'],
|
||||
],
|
||||
'markdown' => [
|
||||
'top' => [' ', ' ', ' ', ' '],
|
||||
'cell' => ['|', ' ', '|', '|'],
|
||||
'middle' => ['|', '-', '|', '|'],
|
||||
'bottom' => [' ', ' ', ' ', ' '],
|
||||
'cross-top' => ['|', ' ', ' ', '|'],
|
||||
'cross-bottom' => ['|', ' ', ' ', '|'],
|
||||
],
|
||||
'borderless' => [
|
||||
'top' => ['=', '=', ' ', '='],
|
||||
'cell' => [' ', ' ', ' ', ' '],
|
||||
'middle' => ['=', '=', ' ', '='],
|
||||
'bottom' => ['=', '=', ' ', '='],
|
||||
'cross-top' => ['=', '=', ' ', '='],
|
||||
'cross-bottom' => ['=', '=', ' ', '='],
|
||||
],
|
||||
'box' => [
|
||||
'top' => ['┌', '─', '┬', '┐'],
|
||||
'cell' => ['│', ' ', '│', '│'],
|
||||
'middle' => ['├', '─', '┼', '┤'],
|
||||
'bottom' => ['└', '─', '┴', '┘'],
|
||||
'cross-top' => ['├', '─', '┴', '┤'],
|
||||
'cross-bottom' => ['├', '─', '┬', '┤'],
|
||||
],
|
||||
'box-double' => [
|
||||
'top' => ['╔', '═', '╤', '╗'],
|
||||
'cell' => ['║', ' ', '│', '║'],
|
||||
'middle' => ['╠', '─', '╪', '╣'],
|
||||
'bottom' => ['╚', '═', '╧', '╝'],
|
||||
'cross-top' => ['╠', '═', '╧', '╣'],
|
||||
'cross-bottom' => ['╠', '═', '╤', '╣'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* 设置表格头信息 以及对齐方式
|
||||
* @access public
|
||||
* @param array $header 要输出的Header信息
|
||||
* @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
|
||||
* @return void
|
||||
*/
|
||||
public function setHeader(array $header, $align = self::ALIGN_LEFT)
|
||||
{
|
||||
$this->header = $header;
|
||||
$this->headerAlign = $align;
|
||||
|
||||
$this->checkColWidth($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输出表格数据 及对齐方式
|
||||
* @access public
|
||||
* @param array $rows 要输出的表格数据(二维数组)
|
||||
* @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
|
||||
* @return void
|
||||
*/
|
||||
public function setRows(array $rows, $align = self::ALIGN_LEFT)
|
||||
{
|
||||
$this->rows = $rows;
|
||||
$this->cellAlign = $align;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$this->checkColWidth($row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查列数据的显示宽度
|
||||
* @access public
|
||||
* @param mixed $row 行数据
|
||||
* @return void
|
||||
*/
|
||||
protected function checkColWidth($row)
|
||||
{
|
||||
if (is_array($row)) {
|
||||
foreach ($row as $key => $cell) {
|
||||
if (!isset($this->colWidth[$key]) || strlen($cell) > $this->colWidth[$key]) {
|
||||
$this->colWidth[$key] = strlen($cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加一行表格数据
|
||||
* @access public
|
||||
* @param mixed $row 行数据
|
||||
* @param bool $first 是否在开头插入
|
||||
* @return void
|
||||
*/
|
||||
public function addRow($row, $first = false)
|
||||
{
|
||||
if ($first) {
|
||||
array_unshift($this->rows, $row);
|
||||
} else {
|
||||
$this->rows[] = $row;
|
||||
}
|
||||
|
||||
$this->checkColWidth($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输出表格的样式
|
||||
* @access public
|
||||
* @param string $style 样式名
|
||||
* @return void
|
||||
*/
|
||||
public function setStyle($style)
|
||||
{
|
||||
$this->style = isset($this->format[$style]) ? $style : 'default';
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出分隔行
|
||||
* @access public
|
||||
* @param string $pos 位置
|
||||
* @return string
|
||||
*/
|
||||
protected function renderSeparator($pos)
|
||||
{
|
||||
$style = $this->getStyle($pos);
|
||||
$array = [];
|
||||
|
||||
foreach ($this->colWidth as $width) {
|
||||
$array[] = str_repeat($style[1], $width + 2);
|
||||
}
|
||||
|
||||
return $style[0] . implode($style[2], $array) . $style[3] . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出表格头部
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
protected function renderHeader()
|
||||
{
|
||||
$style = $this->getStyle('cell');
|
||||
$content = $this->renderSeparator('top');
|
||||
|
||||
foreach ($this->header as $key => $header) {
|
||||
$array[] = ' ' . str_pad($header, $this->colWidth[$key], $style[1], $this->headerAlign);
|
||||
}
|
||||
|
||||
if (!empty($array)) {
|
||||
$content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
|
||||
|
||||
if ($this->rows) {
|
||||
$content .= $this->renderSeparator('middle');
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function getStyle($style)
|
||||
{
|
||||
if ($this->format[$this->style]) {
|
||||
$style = $this->format[$this->style][$style];
|
||||
} else {
|
||||
$style = [' ', ' ', ' ', ' '];
|
||||
}
|
||||
|
||||
return $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出表格
|
||||
* @access public
|
||||
* @param array $dataList 表格数据
|
||||
* @return string
|
||||
*/
|
||||
public function render($dataList = [])
|
||||
{
|
||||
if ($dataList) {
|
||||
$this->setRows($dataList);
|
||||
}
|
||||
|
||||
// 输出头部
|
||||
$content = $this->renderHeader();
|
||||
$style = $this->getStyle('cell');
|
||||
|
||||
if ($this->rows) {
|
||||
foreach ($this->rows as $row) {
|
||||
if (is_string($row) && '-' === $row) {
|
||||
$content .= $this->renderSeparator('middle');
|
||||
} elseif (is_scalar($row)) {
|
||||
$content .= $this->renderSeparator('cross-top');
|
||||
$array = str_pad($row, 3 * (count($this->colWidth) - 1) + array_reduce($this->colWidth, function ($a, $b) {
|
||||
return $a + $b;
|
||||
}));
|
||||
|
||||
$content .= $style[0] . ' ' . $array . ' ' . $style[3] . PHP_EOL;
|
||||
$content .= $this->renderSeparator('cross-bottom');
|
||||
} else {
|
||||
$array = [];
|
||||
|
||||
foreach ($row as $key => $val) {
|
||||
$array[] = ' ' . str_pad($val, $this->colWidth[$key], ' ', $this->cellAlign);
|
||||
}
|
||||
|
||||
$content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$content .= $this->renderSeparator('bottom');
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
1
Server/thinkphp/library/think/console/bin/README.md
Normal file
1
Server/thinkphp/library/think/console/bin/README.md
Normal file
@@ -0,0 +1 @@
|
||||
console 工具使用 hiddeninput.exe 在 windows 上隐藏密码输入,该二进制文件由第三方提供,相关源码和其他细节可以在 [Hidden Input](https://github.com/Seldaek/hidden-input) 找到。
|
||||
BIN
Server/thinkphp/library/think/console/bin/hiddeninput.exe
Normal file
BIN
Server/thinkphp/library/think/console/bin/hiddeninput.exe
Normal file
Binary file not shown.
59
Server/thinkphp/library/think/console/command/Build.php
Normal file
59
Server/thinkphp/library/think/console/command/Build.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
use think\facade\Build as AppBuild;
|
||||
|
||||
class Build extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('build')
|
||||
->setDefinition([
|
||||
new Option('config', null, Option::VALUE_OPTIONAL, "build.php path"),
|
||||
new Option('module', null, Option::VALUE_OPTIONAL, "module name"),
|
||||
])
|
||||
->setDescription('Build Application Dirs');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if ($input->hasOption('module')) {
|
||||
AppBuild::module($input->getOption('module'));
|
||||
$output->writeln("Successed");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->hasOption('config')) {
|
||||
$build = include $input->getOption('config');
|
||||
} else {
|
||||
$build = include App::getAppPath() . 'build.php';
|
||||
}
|
||||
|
||||
if (empty($build)) {
|
||||
$output->writeln("Build Config Is Empty");
|
||||
return;
|
||||
}
|
||||
|
||||
AppBuild::run($build);
|
||||
$output->writeln("Successed");
|
||||
|
||||
}
|
||||
}
|
||||
70
Server/thinkphp/library/think/console/command/Clear.php
Normal file
70
Server/thinkphp/library/think/console/command/Clear.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
use think\facade\Cache;
|
||||
|
||||
class Clear extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this
|
||||
->setName('clear')
|
||||
->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null)
|
||||
->addOption('cache', 'c', Option::VALUE_NONE, 'clear cache file')
|
||||
->addOption('route', 'u', Option::VALUE_NONE, 'clear route cache')
|
||||
->addOption('log', 'l', Option::VALUE_NONE, 'clear log file')
|
||||
->addOption('dir', 'r', Option::VALUE_NONE, 'clear empty dir')
|
||||
->setDescription('Clear runtime file');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if ($input->getOption('route')) {
|
||||
Cache::clear('route_cache');
|
||||
} else {
|
||||
if ($input->getOption('cache')) {
|
||||
$path = App::getRuntimePath() . 'cache';
|
||||
} elseif ($input->getOption('log')) {
|
||||
$path = App::getRuntimePath() . 'log';
|
||||
} else {
|
||||
$path = $input->getOption('path') ?: App::getRuntimePath();
|
||||
}
|
||||
|
||||
$rmdir = $input->getOption('dir') ? true : false;
|
||||
$this->clear(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir);
|
||||
}
|
||||
|
||||
$output->writeln("<info>Clear Successed</info>");
|
||||
}
|
||||
|
||||
protected function clear($path, $rmdir)
|
||||
{
|
||||
$files = is_dir($path) ? scandir($path) : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ('.' != $file && '..' != $file && is_dir($path . $file)) {
|
||||
array_map('unlink', glob($path . $file . DIRECTORY_SEPARATOR . '*.*'));
|
||||
if ($rmdir) {
|
||||
rmdir($path . $file);
|
||||
}
|
||||
} elseif ('.gitignore' != $file && is_file($path . $file)) {
|
||||
unlink($path . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Server/thinkphp/library/think/console/command/Help.php
Normal file
68
Server/thinkphp/library/think/console/command/Help.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument as InputArgument;
|
||||
use think\console\input\Option as InputOption;
|
||||
use think\console\Output;
|
||||
|
||||
class Help extends Command
|
||||
{
|
||||
private $command;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->ignoreValidationErrors();
|
||||
|
||||
$this->setName('help')->setDefinition([
|
||||
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
|
||||
])->setDescription('Displays help for a command')->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command displays help for a given command:
|
||||
|
||||
<info>php %command.full_name% list</info>
|
||||
|
||||
To display the list of available commands, please use the <info>list</info> command.
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command.
|
||||
* @param Command $command The command to set
|
||||
*/
|
||||
public function setCommand(Command $command)
|
||||
{
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if (null === $this->command) {
|
||||
$this->command = $this->getConsole()->find($input->getArgument('command_name'));
|
||||
}
|
||||
|
||||
$output->describe($this->command, [
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
]);
|
||||
|
||||
$this->command = null;
|
||||
}
|
||||
}
|
||||
73
Server/thinkphp/library/think/console/command/Lists.php
Normal file
73
Server/thinkphp/library/think/console/command/Lists.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument as InputArgument;
|
||||
use think\console\input\Definition as InputDefinition;
|
||||
use think\console\input\Option as InputOption;
|
||||
use think\console\Output;
|
||||
|
||||
class Lists extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('list')->setDefinition($this->createDefinition())->setDescription('Lists commands')->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command lists all commands:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
You can also display the commands for a specific namespace:
|
||||
|
||||
<info>php %command.full_name% test</info>
|
||||
|
||||
It's also possible to get raw list of commands (useful for embedding command runner):
|
||||
|
||||
<info>php %command.full_name% --raw</info>
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNativeDefinition()
|
||||
{
|
||||
return $this->createDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$output->describe($this->getConsole(), [
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
'namespace' => $input->getArgument('namespace'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
private function createDefinition()
|
||||
{
|
||||
return new InputDefinition([
|
||||
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
110
Server/thinkphp/library/think/console/command/Make.php
Normal file
110
Server/thinkphp/library/think/console/command/Make.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
use think\facade\Config;
|
||||
use think\facade\Env;
|
||||
|
||||
abstract class Make extends Command
|
||||
{
|
||||
protected $type;
|
||||
|
||||
abstract protected function getStub();
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', Argument::REQUIRED, "The name of the class");
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
|
||||
$name = trim($input->getArgument('name'));
|
||||
|
||||
$classname = $this->getClassName($name);
|
||||
|
||||
$pathname = $this->getPathName($classname);
|
||||
|
||||
if (is_file($pathname)) {
|
||||
$output->writeln('<error>' . $this->type . ' already exists!</error>');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_dir(dirname($pathname))) {
|
||||
mkdir(dirname($pathname), 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents($pathname, $this->buildClass($classname));
|
||||
|
||||
$output->writeln('<info>' . $this->type . ' created successfully.</info>');
|
||||
|
||||
}
|
||||
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$stub = file_get_contents($this->getStub());
|
||||
|
||||
$namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
|
||||
|
||||
$class = str_replace($namespace . '\\', '', $name);
|
||||
|
||||
return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], [
|
||||
$class,
|
||||
Config::get('action_suffix'),
|
||||
$namespace,
|
||||
App::getNamespace(),
|
||||
], $stub);
|
||||
}
|
||||
|
||||
protected function getPathName($name)
|
||||
{
|
||||
$name = str_replace(App::getNamespace() . '\\', '', $name);
|
||||
|
||||
return Env::get('app_path') . ltrim(str_replace('\\', '/', $name), '/') . '.php';
|
||||
}
|
||||
|
||||
protected function getClassName($name)
|
||||
{
|
||||
$appNamespace = App::getNamespace();
|
||||
|
||||
if (strpos($name, $appNamespace . '\\') !== false) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (Config::get('app_multi_module')) {
|
||||
if (strpos($name, '/')) {
|
||||
list($module, $name) = explode('/', $name, 2);
|
||||
} else {
|
||||
$module = 'common';
|
||||
}
|
||||
} else {
|
||||
$module = null;
|
||||
}
|
||||
|
||||
if (strpos($name, '/') !== false) {
|
||||
$name = str_replace('/', '\\', $name);
|
||||
}
|
||||
|
||||
return $this->getNamespace($appNamespace, $module) . '\\' . $name;
|
||||
}
|
||||
|
||||
protected function getNamespace($appNamespace, $module)
|
||||
{
|
||||
return $module ? ($appNamespace . '\\' . $module) : $appNamespace;
|
||||
}
|
||||
|
||||
}
|
||||
130
Server/thinkphp/library/think/console/command/RouteList.php
Normal file
130
Server/thinkphp/library/think/console/command/RouteList.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\console\Table;
|
||||
use think\Container;
|
||||
|
||||
class RouteList extends Command
|
||||
{
|
||||
protected $sortBy = [
|
||||
'rule' => 0,
|
||||
'route' => 1,
|
||||
'method' => 2,
|
||||
'name' => 3,
|
||||
'domain' => 4,
|
||||
];
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('route:list')
|
||||
->addArgument('style', Argument::OPTIONAL, "the style of the table.", 'default')
|
||||
->addOption('sort', 's', Option::VALUE_OPTIONAL, 'order by rule name.', 0)
|
||||
->addOption('more', 'm', Option::VALUE_NONE, 'show route options.')
|
||||
->setDescription('show route list.');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$filename = Container::get('app')->getRuntimePath() . 'route_list.php';
|
||||
|
||||
if (is_file($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
$content = $this->getRouteList();
|
||||
file_put_contents($filename, 'Route List' . PHP_EOL . $content);
|
||||
}
|
||||
|
||||
protected function getRouteList()
|
||||
{
|
||||
Container::get('route')->setTestMode(true);
|
||||
// 路由检测
|
||||
$path = Container::get('app')->getRoutePath();
|
||||
|
||||
$files = is_dir($path) ? scandir($path) : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (strpos($file, '.php')) {
|
||||
$filename = $path . DIRECTORY_SEPARATOR . $file;
|
||||
// 导入路由配置
|
||||
$rules = include $filename;
|
||||
|
||||
if (is_array($rules)) {
|
||||
Container::get('route')->import($rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Container::get('config')->get('route_annotation')) {
|
||||
$suffix = Container::get('config')->get('controller_suffix') || Container::get('config')->get('class_suffix');
|
||||
|
||||
include Container::get('build')->buildRoute($suffix);
|
||||
}
|
||||
|
||||
$table = new Table();
|
||||
|
||||
if ($this->input->hasOption('more')) {
|
||||
$header = ['Rule', 'Route', 'Method', 'Name', 'Domain', 'Option', 'Pattern'];
|
||||
} else {
|
||||
$header = ['Rule', 'Route', 'Method', 'Name', 'Domain'];
|
||||
}
|
||||
|
||||
$table->setHeader($header);
|
||||
|
||||
$routeList = Container::get('route')->getRuleList();
|
||||
$rows = [];
|
||||
|
||||
foreach ($routeList as $domain => $items) {
|
||||
foreach ($items as $item) {
|
||||
$item['route'] = $item['route'] instanceof \Closure ? '<Closure>' : $item['route'];
|
||||
|
||||
if ($this->input->hasOption('more')) {
|
||||
$item = [$item['rule'], $item['route'], $item['method'], $item['name'], $domain, json_encode($item['option']), json_encode($item['pattern'])];
|
||||
} else {
|
||||
$item = [$item['rule'], $item['route'], $item['method'], $item['name'], $domain];
|
||||
}
|
||||
|
||||
$rows[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->input->getOption('sort')) {
|
||||
$sort = $this->input->getOption('sort');
|
||||
|
||||
if (isset($this->sortBy[$sort])) {
|
||||
$sort = $this->sortBy[$sort];
|
||||
}
|
||||
|
||||
uasort($rows, function ($a, $b) use ($sort) {
|
||||
$itemA = isset($a[$sort]) ? $a[$sort] : null;
|
||||
$itemB = isset($b[$sort]) ? $b[$sort] : null;
|
||||
|
||||
return strcasecmp($itemA, $itemB);
|
||||
});
|
||||
}
|
||||
|
||||
$table->setRows($rows);
|
||||
|
||||
if ($this->input->getArgument('style')) {
|
||||
$style = $this->input->getArgument('style');
|
||||
$table->setStyle($style);
|
||||
}
|
||||
|
||||
return $this->table($table);
|
||||
}
|
||||
|
||||
}
|
||||
53
Server/thinkphp/library/think/console/command/RunServer.php
Normal file
53
Server/thinkphp/library/think/console/command/RunServer.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: Slince <taosikai@yeah.net>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
|
||||
class RunServer extends Command
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('run')
|
||||
->addOption('host', 'H', Option::VALUE_OPTIONAL,
|
||||
'The host to server the application on', '127.0.0.1')
|
||||
->addOption('port', 'p', Option::VALUE_OPTIONAL,
|
||||
'The port to server the application on', 8000)
|
||||
->addOption('root', 'r', Option::VALUE_OPTIONAL,
|
||||
'The document root of the application', App::getRootPath() . 'public')
|
||||
->setDescription('PHP Built-in Server for ThinkPHP');
|
||||
}
|
||||
|
||||
public function execute(Input $input, Output $output)
|
||||
{
|
||||
$host = $input->getOption('host');
|
||||
$port = $input->getOption('port');
|
||||
$root = $input->getOption('root');
|
||||
|
||||
$command = sprintf(
|
||||
'php -S %s:%d -t %s %s',
|
||||
$host,
|
||||
$port,
|
||||
escapeshellarg($root),
|
||||
escapeshellarg($root . DIRECTORY_SEPARATOR . 'router.php')
|
||||
);
|
||||
|
||||
$output->writeln(sprintf('ThinkPHP Development server is started On <http://%s:%s/>', $host, $port));
|
||||
$output->writeln(sprintf('You can exit with <info>`CTRL-C`</info>'));
|
||||
$output->writeln(sprintf('Document root is: %s', $root));
|
||||
passthru($command);
|
||||
}
|
||||
|
||||
}
|
||||
31
Server/thinkphp/library/think/console/command/Version.php
Normal file
31
Server/thinkphp/library/think/console/command/Version.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
|
||||
class Version extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('version')
|
||||
->setDescription('show thinkphp framework version');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$output->writeln('v' . App::version());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
use think\console\command\Make;
|
||||
use think\console\input\Argument;
|
||||
use think\facade\App;
|
||||
|
||||
class Command extends Make
|
||||
{
|
||||
protected $type = "Command";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('make:command')
|
||||
->addArgument('commandName', Argument::OPTIONAL, "The name of the command")
|
||||
->setDescription('Create a new command class');
|
||||
}
|
||||
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$commandName = $this->input->getArgument('commandName') ?: strtolower(basename($name));
|
||||
$namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
|
||||
|
||||
$class = str_replace($namespace . '\\', '', $name);
|
||||
$stub = file_get_contents($this->getStub());
|
||||
|
||||
return str_replace(['{%commandName%}', '{%className%}', '{%namespace%}', '{%app_namespace%}'], [
|
||||
$commandName,
|
||||
$class,
|
||||
$namespace,
|
||||
App::getNamespace(),
|
||||
], $stub);
|
||||
}
|
||||
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'command.stub';
|
||||
}
|
||||
|
||||
protected function getNamespace($appNamespace, $module)
|
||||
{
|
||||
return $appNamespace . '\\command';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
use think\console\command\Make;
|
||||
use think\console\input\Option;
|
||||
use think\facade\Config;
|
||||
|
||||
class Controller extends Make
|
||||
{
|
||||
protected $type = "Controller";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('make:controller')
|
||||
->addOption('api', null, Option::VALUE_NONE, 'Generate an api controller class.')
|
||||
->addOption('plain', null, Option::VALUE_NONE, 'Generate an empty controller class.')
|
||||
->setDescription('Create a new resource controller class');
|
||||
}
|
||||
|
||||
protected function getStub()
|
||||
{
|
||||
$stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if ($this->input->getOption('api')) {
|
||||
return $stubPath . 'controller.api.stub';
|
||||
}
|
||||
|
||||
if ($this->input->getOption('plain')) {
|
||||
return $stubPath . 'controller.plain.stub';
|
||||
}
|
||||
|
||||
return $stubPath . 'controller.stub';
|
||||
}
|
||||
|
||||
protected function getClassName($name)
|
||||
{
|
||||
return parent::getClassName($name) . (Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '');
|
||||
}
|
||||
|
||||
protected function getNamespace($appNamespace, $module)
|
||||
{
|
||||
return parent::getNamespace($appNamespace, $module) . '\controller';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
use think\console\command\Make;
|
||||
|
||||
class Middleware extends Make
|
||||
{
|
||||
protected $type = "Middleware";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('make:middleware')
|
||||
->setDescription('Create a new middleware class');
|
||||
}
|
||||
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'middleware.stub';
|
||||
}
|
||||
|
||||
protected function getNamespace($appNamespace, $module)
|
||||
{
|
||||
return parent::getNamespace($appNamespace, 'http') . '\middleware';
|
||||
}
|
||||
}
|
||||
36
Server/thinkphp/library/think/console/command/make/Model.php
Normal file
36
Server/thinkphp/library/think/console/command/make/Model.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
use think\console\command\Make;
|
||||
|
||||
class Model extends Make
|
||||
{
|
||||
protected $type = "Model";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('make:model')
|
||||
->setDescription('Create a new model class');
|
||||
}
|
||||
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'model.stub';
|
||||
}
|
||||
|
||||
protected function getNamespace($appNamespace, $module)
|
||||
{
|
||||
return parent::getNamespace($appNamespace, $module) . '\model';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
use think\console\command\Make;
|
||||
|
||||
class Validate extends Make
|
||||
{
|
||||
protected $type = "Validate";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('make:validate')
|
||||
->setDescription('Create a validate class');
|
||||
}
|
||||
|
||||
protected function getStub()
|
||||
{
|
||||
$stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
|
||||
|
||||
return $stubPath . 'validate.stub';
|
||||
}
|
||||
|
||||
protected function getNamespace($appNamespace, $module)
|
||||
{
|
||||
return parent::getNamespace($appNamespace, $module) . '\validate';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace {%namespace%};
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class {%className%} extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('{%commandName%}');
|
||||
// 设置参数
|
||||
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
// 指令输出
|
||||
$output->writeln('{%commandName%}');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace {%namespace%};
|
||||
|
||||
use think\Controller;
|
||||
use think\Request;
|
||||
|
||||
class {%className%} extends Controller
|
||||
{
|
||||
/**
|
||||
* 显示资源列表
|
||||
*
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function index{%actionSuffix%}()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存新建的资源
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function save{%actionSuffix%}(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示指定的资源
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function read{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存更新的资源
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function update{%actionSuffix%}(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定资源
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function delete{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace {%namespace%};
|
||||
|
||||
use think\Controller;
|
||||
|
||||
class {%className%} extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace {%namespace%};
|
||||
|
||||
use think\Controller;
|
||||
use think\Request;
|
||||
|
||||
class {%className%} extends Controller
|
||||
{
|
||||
/**
|
||||
* 显示资源列表
|
||||
*
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function index{%actionSuffix%}()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示创建资源表单页.
|
||||
*
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function create{%actionSuffix%}()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存新建的资源
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function save{%actionSuffix%}(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示指定的资源
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function read{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示编辑资源表单页.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function edit{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存更新的资源
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function update{%actionSuffix%}(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定资源
|
||||
*
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function delete{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace {%namespace%};
|
||||
|
||||
class {%className%}
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user