对天乙社区bbscs8实现的详细分析十二

此文为转载:http://www.diybl.com/course/1_web/webjs/2007113/82989.html

经过前面的分析,我们已经理清楚了业务层,接下来的部分将是web层部分.首先我们从web.xml开始,我们知

道任何一个java web应用系统都是从WEB-INF/web.xml启动的,根据servlet2.4规范filter执行是按照

web.xml配置的filter-mapping先后顺序进行执行,这里用了UrlRewriteFilter和字符编码过滤器

CharacterEncodingFilter(UTF-8),还有配置延迟加载时使用OpenSessionInView,可参考资料

http://www.javaeye.com/topic/32001;另外,还有struts-clearup,以及Strut2的

org.apache.struts2.dispatcher.FilterDispatcher,注意它有两个dipatcher参数, 在这种情况下,如

果请求是以/*开头的,并且是通过request   dispatcher的forward方法传递过来或者直接从客户端传递

过来的,则必须经过这个过滤器, Servlet 2.3 版新增了Filter的功能,不过它只能由客户端发出请求

来调用Filter,但若使用   RequestDispatcher.forward( )或RequestDispatcher.include( )的方法调

用Filter 时,Filter 却不会执行.这个功能应该都是主要用于UrlRewrite用的吧.而<context-param>元

素定义了一些应用级的参数,如:urlrewrite,cluster都为false,servletmapping为

*.bbscs,poststoragemode为1;接下来是listener有两个,一是

com.loaer.bbscs.web.servlet.SysListener就是对上面的变量进行操作的一个监听器,而另一个则是

spring的ContextLoaderListener,这里我们不讨论,接下来是几个servlet,提供一些常用的功能:authimg

生成验证码,rss.另外还有welcome-file及struts-tag.tld的taglib和一些error-page,如:error-

code:401--->location:-->/401.htm.对于具体的加载过程可见启动resin3等服务器后的控制台显示,我个

人觉得是加载了应用级参数,再是2个Linstener..,到spring-->加载所有bean到时空器-->各个bean的加载

(包括hibernate的配置等)--->ContextLoader启动完成-->接下来,对Filter按相反的顺序加载(struts-

>struts-clean-->characterEncoding)先是struts2的配置文件的加载,还有global messages...
注意这段时间内也会启动一些TimerTask任务...,这点可从日志中看到,最后是

ObjectTypeDeterminerFactory应该是用于struts-clean吧.还有

OpenSessionInViewFilter,CharacterEncodingFilter,UrlRewriteFilter...
这样,应用和resin服务才开始发挥作用,才能访问!
好的,我们先看在com.laoer.bbscs.web.servlet包中的几个源文件:
SysListener:它继承自HttpServlet,实现了ServletContextLinster接口.
String rootpath = sce.getServletContext().getRealPath("/");
   if (rootpath != null) {
   rootpath = rootpath.replaceAll("//", "/");
} else {
   rootpath = "/";
}
if (!rootpath.endsWith("/")) {
   rootpath = rootpath + "/";
}
Constant.ROOTPATH = rootpath; 记录在常量java文件中.public static String

ROOTPATH = "";
我们看一个代表性的示例源码:
String poststoragemodes = sce.getServletContext().getInitParameter("poststoragemode");
if (poststoragemodes == null) {
   poststoragemodes = "0";
}
Constant.POST_STORAGE_MODE = NumberUtils.toInt(poststoragemodes, 0);//文章存

储格式(这里是文件)
-->
<context-param>
    <param-name>poststoragemode</param-name>
    <param-value>1</param-value>
</context-param>
接下来,我们分析AuthImg,主要用它的doGet方法:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws

ServletException, IOException {
response.setContentType("image/jpeg");
ServletOutputStream out = response.getOutputStream();

int width = 60, height = 20;
BufferedImage image = new BufferedImage(width, height,

BufferedImage.TYPE_INT_RGB);
//这段代码创建了一个 BufferedImage 对象,它代表一个 60像素宽、20像素高的图像。为了应用这个

图像,我们需要有图形上下文,而 BufferedImage 对象的 createGraphics() 方法就返回一个与该图像

相关的 Graphics2D 对象:


Graphics g = image.getGraphics();
Random random = new Random();

g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height); //背景色

g.setFont(mFont); //字体private Font mFont = new Font("Times New Roman",

Font.PLAIN, 18);

g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
   int x = random.nextInt(width);
   int y = random.nextInt(height);
   int xl = random.nextInt(12);
   int yl = random.nextInt(12);
   g.drawLine(x, y, x + xl, y + yl);//画线155条
}

String rand = RandomStringUtils.randomNumeric(4);//产生四个随机数
char c;
for (int i = 0; i < 4; i++) {
   c = rand.charAt(i);
   g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt

(110), 20 + random.nextInt(110))); //各个数的着色不一样
   g.drawString(String.valueOf(c), 13 * i + 6, 16);
}
WebApplicationContext wc =

WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());//
webapplicationcontextutils.getwebapplicationcontext(servletcontext); 如果没有直接返回null
SysConfig sysConfig = (SysConfig) wc.getBean("sysConfig");//web层得到

sysConfig
UserCookie uc = new UserCookie(request, response, sysConfig);//写入Cookie中
uc.addAuthCode(rand);

JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);//也可以用

ImageIO.write(bi,"jpg",out);
encoder.encode(image);
out.close();
}
接下来,看RSS:它也是用在doGet,有两种,一种是首页,不带bid参数,一种是带bid.用于各个版区的...
long bid;
try {
   bid = Long.parseLong(request.getParameter("bid"));
} catch (NumberFormatException e) {
   bid = 0L;
}
首先为了使用服务,这里用了spring的WebApplicationContext wc =

WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());这样我们就可以

得到ForumService和SysConfig,BoardService..这里用了下SyndFeed feed=new SynFeedImpl();
com.sun.syndication这个包,接着便是feed.setFeedType("rss_2.0");先设置好feed源的标题/链接(有

两种,分有bid和没bid)/描述,注意这些参数大多来自于sysConfig,接下来,我们设置entry(条目)及其

description
List<SyndEntry> entries = new ArrayList<SyndEntry>();
   SyndEntry entry;
   SyndContent description;
我们看后半部分的代码:
Board board = boardService.getBoardByID(bid);//得到board
   if (board != null) {
    if (board.getBoardType() == 2 || board.getBoardType() == 3)

{
     String forumLink = "";
     try {
      forumLink = BBSCSUtil.absoluteActionURL

(request, "/forum.bbscs?action=index&bid=" + bid)
        .toString();//RSS源链接
     } catch (MalformedURLException ex2) {
      forumLink = "";
     }
     feed.setTitle(sysConfig.getForumName() + " - " +

board.getBoardName());
     feed.setLink(forumLink);
     feed.setDescription(sysConfig.getWebName() + " - " +

sysConfig.getForumName() + " - "
       + board.getBoardName());

     List<SyndEntry> entries = new ArrayList<SyndEntry>

();//所有条目
     SyndEntry entry;
     SyndContent description;

     Pages pages = new Pages();//构造一个Pages对象
     pages.setPage(1);
     pages.setPerPageNum(40);
     pages.setFileName("");

     PageList pl = forumService.findForumsMainWWW(bid,

pages);//重点的地方
     List flist = pl.getObjectList();

     for (int i = 0; i < flist.size(); i++) {
      Forum f = (Forum) flist.get(i);
      try {
       postLink =

BBSCSUtil.absoluteActionURL(request,
         "/main.bbscs?

action=read&bid=" + f.getBoardID() + "&postID=" + f.getMainID())
         .toString();
      } catch (MalformedURLException ex) {
       postLink = "";
      }

      entry = new SyndEntryImpl();
      entry.setTitle(f.getTitle());
      entry.setLink(postLink);
      entry.setPublishedDate(new Date

(f.getPostTime()));

      description = new SyndContentImpl();
      if (f.getEditType() == 0) {
       description.setType("text/plain");//

文本类型
      } else {
       description.setType("text/html");
      }
      description.setValue(BBSCSUtil
        .getSpeShortString

(forumService.getForumDetail(f, false), 400, ""));//格式化
      entry.setDescription(description);
      entries.add(entry);
     }

     feed.setEntries(entries);
     try {
      SyndFeedOutput output = new SyndFeedOutput

();
     
      Document messagesDocument =

output.outputJDom(feed);
      Format format = Format.getPrettyFormat();
      format.setOmitDeclaration(true);
      XMLOutputter xmlo = new XMLOutputter

(format);
      xmlo.output(messagesDocument, out);
     } catch (Exception ex) {
      logger.error(ex);
     }
    }
   }

其中:
public static URL absoluteActionURL(HttpServletRequest request, String action) throws

MalformedURLException {
return new URL(RequestUtils.serverURL(request) + getActionMappingURL(action,

request));
}
--->
public static String getActionMappingURL(String action, HttpServletRequest request) {

StringBuffer value = new StringBuffer(request.getContextPath());//获得的当前

目录路径

// Use our servlet mapping, if one is specified
String servletMapping = Constant.SERVLET_MAPPING;//*.bbscs
if (servletMapping != null) {
   String queryString = null;
   int question = action.indexOf("?");//action="/main.bbscs?

action=read&bid=" + f.getBoardID() + "&postID=" + f.getMainID()).toString();
   if (question >= 0) {
    queryString = action.substring(question);//?

action=read&bid=12&postID=123123
   }
   String actionMapping = getActionMappingName(action);// /main
   if (servletMapping.startsWith("*.")) {
    value.append(actionMapping);
    value.append(servletMapping.substring(1));

//value=/main.bbcs
   } else if (servletMapping.endsWith("/*")) {
    value.append(servletMapping.substring(0,

servletMapping.length() - 2));
    value.append(actionMapping);
   } else if (servletMapping.equals("/")) {
    value.append(actionMapping);
   }
   if (queryString != null) {
    value.append(queryString);
   }
}
--->
public static String getActionMappingName(String action) {
String value = action;
int question = action.indexOf("?");
if (question >= 0) {
   value = value.substring(0, question);// /main.bbscs
}
int slash = value.lastIndexOf("/");
int period = value.lastIndexOf(".");
if ((period >= 0) && (period > slash)) {
   value = value.substring(0, period);// /main
}
if (value.startsWith("/")) {
   return (value);
} else {
   return ("/" + value);
}
}
OK!接下来,我们看看登录流程先开始分析吧!
在浏览器在输入http://localhost:8080/bbscs8启动:
<welcome-file-list>
    <welcome-file>index.jsp</welcome-file> //相对当前目录哦!!!
</welcome-file-list>
index.jsp触发了action login.bbscs?action=check:
<script language="javascript" type="text/javascript">
window.location.href="login.bbscs?action=check";
</script>
</head>
-->struts2发生作用...
我们看下struts.properties:
struts.devMode=false
struts.action.extension=bbscs //后缀
struts.enable.DynamicMethodInvocation=true
struts.i18n.reload=true
struts.ui.theme=simple

struts.locale=zh_CN
struts.i18n.encoding=UTF-8
struts.objectFactory=spring   //由spring来代理bean
struts.objectFactory.spring.autoWire=name

struts.serve.static.browserCache=false
struts.url.includeParams=none

struts.custom.i18n.resources=com.laoer.bbscs.web.action.BaseAction //资源文件!
看后台的输出日志[com.opensymphony.xwork2.validator.ActionValidatorManagerFactory]-[INFO]

Detected AnnotationActionValidatorManager, initializing it... (注意:并不一定是这里触发哦!
接着我们看struts.xml,其中name为bbscs-default的package继承之struts-default,并引入了许多自定义

的interceptor和interceptor-stack.也设置了global-results...这些是为它们package使用的.
<package name="loginout" extends="bbscs-default" namespace="/">
<action name="login" class="loginAction">
   <result name="success" type="redirect">${tourl}</result>
   <result name="input">/WEB-INF/jsp/login.jsp</result>
   <result name="loginPass">/WEB-INF/jsp/passLogin.jsp</result>
   <interceptor-ref name="defaultStack"></interceptor-ref>//一旦继承了

struts-default包(package),所有Action都会调用拦截器栈 ——defaultStack。当然,在Action配置

中加入“<interceptor-ref name="xx" />”可以覆盖defaultStack。
   <interceptor-ref name="remoteAddrInterceptor"></interceptor-ref>
   <interceptor-ref name="userCookieInterceptor"></interceptor-ref>
   <interceptor-ref name="requestBasePathInterceptor"></interceptor-

ref>
</action>
</package>
注意这里的login对应于spring配置文件action-servlet.xml中的loginAction:
<bean id="loginAction" class="com.laoer.bbscs.web.action.Login" //所管理的action bean
scope="prototype" autowire="byName"> //用了autowire就不用下面这段了
<!--
   <property name="sysConfig">
   <ref bean="sysConfig" />
   </property>
-->
</bean>
好的,我们先看拦截器:<interceptor-ref name="defaultStack"></interceptor-ref>使用默认的拦截器

栈(在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用),我们可以打开

struts2.0core包找到struts-default.xml打开找到<interceptors>元素内容..请看资

料:http://www.blogjava.net/max/archive/2006/12/06/85925.html
<interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="profiling"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="params">
                  <param name="excludeParams">dojo..*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
            </interceptor-stack>
而自定义拦截器是要求拦截器是无状态的原因是Struts 2不能保证为每一个请求或者action创建一个实例

,所以如果拦截器带有状态,会引发并发问题。所有的Struts 2的拦截器都直接或间接实现接口

com.opensymphony.xwork2.interceptor.Interceptor。除此之外,大家可能更喜欢继承类

com.opensymphony.xwork2.interceptor.AbstractInterceptor。需实现其public String intercept

(ActionInvocation invocation) throws Exception .
而下面的remoteAddrInterceptor:
<interceptor name="remoteAddrInterceptor"
   class="com.laoer.bbscs.web.interceptor.RemoteAddrInterceptor">
   </interceptor>
我们进入web.interceptor层:
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext ac = invocation.getInvocationContext();
Object action = invocation.getAction();
HttpServletRequest request = (HttpServletRequest) ac.get

(ServletActionContext.HTTP_REQUEST);//得到request请求
String userRemoteAddr = request.getRemoteAddr();
if (action instanceof RemoteAddrAware) { //action是RomoteAddrAware实例?

   ((RemoteAddrAware)action).setRemoteAddr(userRemoteAddr);
   //System.out.println(userRemoteAddr);
}

return invocation.invoke();
}
}
我们可以看到RemoteAddrAware是如下这个接口,这是为了方便将远程地址放入action中:
public interface RemoteAddrAware {
public void setRemoteAddr(String remoteAddr);
}

接下来是userCookieInterceptor:
<interceptor name="userCookieInterceptor"   
class="com.laoer.bbscs.web.interceptor.UserCookieInterceptor">
   </interceptor>
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext ac = invocation.getInvocationContext();
Object action = invocation.getAction();

if (action instanceof UserCookieAware) {
   HttpServletRequest request = (HttpServletRequest) ac.get

(ServletActionContext.HTTP_REQUEST); //用于UserCookie
   HttpServletResponse response = (HttpServletResponse) ac.get

(ServletActionContext.HTTP_RESPONSE);//用于UserCookie

   ServletContext servletContext = (ServletContext) ac.get

(ServletActionContext.SERVLET_CONTEXT);
   WebApplicationContext wc =

WebApplicationContextUtils.getWebApplicationContext(servletContext);//得到业务层服务!
   if (wc == null) {
    logger.error("ApplicationContext could not be found.");
   } else {
    SysConfig sysConfig = (SysConfig) wc.getBean("sysConfig");
    UserCookie userCookie = new UserCookie(request, response,

sysConfig);   //关键点!!!!
    //logger.debug("userCookie sid:" + userCookie.getSid());
    ((UserCookieAware) action).setUserCookie(userCookie);
   }
}
return invocation.invoke();
}
而UserCookieAware:
public interface UserCookieAware {
public void setUserCookie(UserCookie userCookie);
}
看最后一个interceptor:requestBasePathInterceptor
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext ac = invocation.getInvocationContext();
Object action = invocation.getAction();

if (action instanceof RequestBasePathAware) {
   HttpServletRequest request = (HttpServletRequest) ac.get

(ServletActionContext.HTTP_REQUEST);
   StringBuffer sb = new StringBuffer();
   sb.append(BBSCSUtil.getWebRealPath(request));//得到域名
/**
public static String getWebRealPath(HttpServletRequest request) {
StringBuffer sb = new StringBuffer();
sb.append("http://");
sb.append(request.getServerName());
if (request.getServerPort() != 80) {
   sb.append(":");
   sb.append(request.getServerPort());
}
return sb.toString(); //返回域名啊!
}
*/
   sb.append(request.getContextPath());//request相对路径
   sb.append("/");
   ((RequestBasePathAware) action).setBasePath(sb.toString());//设置

BasePath
}

return invocation.invoke();
}
其中,RequestBasePathAware:
public interface RequestBasePathAware {
public void setBasePath(String basePath);
}

我们回到public class Login extends BaseAction implements RequestBasePathAware,

RemoteAddrAware, UserCookieAware, SessionAware,可见这个Login实现了我们的这些Aware..并且它继

承了BaseAction,而BaseAction继承了ActionSupport,它有几个通用的方

法:getAction,setAction,getAjax,setAjax,及page.total(本来私有的)的getter/setter方法,另外还有

以下方法:
protected String executeMethod(String method) throws Exception { //子类用!
Class[] c = null;
Method m = this.getClass().getMethod(method, c);
Object[] o = null;
String result = (String) m.invoke(this, o);
return result;
}

public int boolean2int(boolean value) {
if (value) {
   return 1;
} else {
   return 0;
}
}

public boolean int2boolean(int value) {
if (value == 0) {
   return false;
} else {
   return true;
}
}
有点类似C++了!true-->1 value!=0--->true
我们进入正题Login:
首先它需要一个静态的logger:private static final Log logger = LogFactory.getLog(Login.class);
还有private static final long serivalVeserionUID...
当然,它需要get/set一下上面的basePath,remoteAddr,userCookie.另外还有一个session
作为struts,它有与表单交互的字

段:actionUrl,tourl,passwd,username,hiddenLogin,authCode,urlRewrite,useAuthCode,cookieTime=-1

等及其getter/setter方法...注意:
public boolean isUseAuthCode() {
return useAuthCode;
}
另外,我们可以看到其构造方法中:
public Login() {
this.setRadioYesNoListValues();//隐身选择是或否
this.setCookieTimeListValues();//Cookie时间选择一年/一月/一天/浏览器进程
}
private void setRadioYesNoListValues() { //private的注意哦!!
radioYesNoList.add(new RadioInt(0, this.getText("bbscs.no")));//注意getText

从资源文件BaseAction中获得字符串值!
radioYesNoList.add(new RadioInt(1, this.getText("bbscs.yes")));
}
private void setCookieTimeListValues() {
cookieTimeList.add(new RadioInt(365 * 24 * 3600, this.getText

("login.cookietime0")));//一年以365计算
cookieTimeList.add(new RadioInt(30 * 24 * 3600, this.getText

("login.cookietime1")));
cookieTimeList.add(new RadioInt(24 * 3600, this.getText

("login.cookietime2")));
cookieTimeList.add(new RadioInt(-1, this.getText("login.cookietime3")));
}
我们来看RadioInt(com.laoer.bbscs.web.ui):它是一个简单的bean,封装了两个属性int的key和String类

型的value,而公开其getter/setter方法,和下面的构造方法:
public RadioInt(int key, String value) {
this.key = key;
this.value = value;
}
当然,也有其List<RadioInt> radioYesNoList = new ArrayList<RadioInt>();

public List<RadioInt> getRadioYesNoList() {
return radioYesNoList;
}
public void setRadioYesNoList(List<RadioInt> radioYesNoList) {
this.radioYesNoList = radioYesNoList;
}
也于提供给界面用.而private只能用于类的构造之中.对于一个action,它将调用业务层来处理数据,完成

逻辑操作!这里用到了sysConfig,userService,loginErrorService,userOnlineService,在这个action类

中提供get/set,由spring的applicationContext.xml注入!我们先看
<bean id="sysConfig"
class="com.laoer.bbscs.service.config.SysConfig">
<constructor-arg>
   <ref bean="configService" />
</constructor-arg>
<property name="isLoad">
   <value>${bbscs.isloadconfig}</value> //bbscs.isloadconfig=false
</property>
</bean>
而我们看<bean id="configService" parent="txProxyTemplate"> //其它如userService都类似哦!!
<property name="target">
   <ref bean="configTarget" />
</property>
</bean>
而txProxyTemplate是一个事务处理的TransactionProxyFactoryBean:
<bean id="txProxyTemplate" abstract="true"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
   <ref bean="myTransactionManager" />
</property>
<property name="transactionAttributes"> //对于如下的内容进行事务
   <props>
    <prop key="create*">
     PROPAGATION_REQUIRED,-

com.laoer.bbscs.exception.BbscsException
    </prop>
    <prop key="save*">
     PROPAGATION_REQUIRED,-

com.laoer.bbscs.exception.BbscsException
    </prop>
    <prop key="remove*">
     PROPAGATION_REQUIRED,-

com.laoer.bbscs.exception.BbscsException
    </prop>
    <prop key="update*">
     PROPAGATION_REQUIRED,-

com.laoer.bbscs.exception.BbscsException
    </prop>
    <prop key="del*">
     PROPAGATION_REQUIRED,-

com.laoer.bbscs.exception.BbscsException//出错,报BbscsException
    </prop>
    <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>//只读
   </props>
</property>
</bean>
-->
<bean id="myTransactionManager" //事务管理器
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
   <ref local="sessionFactory" />
</property>
</bean>
-->
<bean id="sessionFactory" //session工厂
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
   <ref local="dataSource" /> //dataSource!!数据源bean.
</property>
<property name="mappingResources">
   <list>
    <value>com/laoer/bbscs/bean/UserInfo.hbm.xml</value>
     ........
    <value> com/laoer/bbscs/bean/Elite-{datasource.type}.hbm.xml
    </value>
   </list>
</property>
<property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">
     ${hibernate.dialect}
    </prop>
    <prop key="hibernate.show_sql">
     ${hibernate.show_sql}
    </prop>
    <prop key="hibernate.jdbc.fetch_size">
     ${hibernate.jdbc.fetch_size}
    </prop>
    <prop key="hibernate.jdbc.batch_size">
     ${hibernate.jdbc.batch_size}
    </prop>
   </props>
</property>
</bean>

计算八字神煞需要用到农历和天干地支的知识,可以使用 PHP 的日期和时间函数来实现。 步骤如下: 1. 获取用户输入的阳历生日,使用 PHP 的 date 函数将其转换为农历生日。 2. 根据农历生日,计算出生年、生月、生日的天干地支。 3. 根据天干地支,查找对应的八字神煞。 以下是一个简单的 PHP 代码示例: ```php // 获取用户输入的生日 $birthday = '1990-01-01'; // 将阳历生日转换为农历生日 $chineseBirthday = getChineseBirthday($birthday); // 计算出生年、月、日的天干地支 list($yearTianGan, $yearDiZhi) = getTianGanDiZhi($chineseBirthday['year']); list($monthTianGan, $monthDiZhi) = getTianGanDiZhi($chineseBirthday['month']); list($dayTianGan, $dayDiZhi) = getTianGanDiZhi($chineseBirthday['day']); // 查找八字神煞 $shenSha = getShenSha($yearTianGan, $yearDiZhi, $monthTianGan, $monthDiZhi, $dayTianGan, $dayDiZhi); // 输出结果 echo '您的八字神煞为:' . implode(',', $shenSha); // 获取农历生日 function getChineseBirthday($birthday) { // 使用 PHP 的 DateTime 类将阳历生日转换为农历生日 $dateTime = new DateTime($birthday); $chineseCalendar = new ChineseCalendar($dateTime); $chineseBirthday = [ 'year' => $chineseCalendar->getChineseYear(), 'month' => $chineseCalendar->getChineseMonth(), 'day' => $chineseCalendar->getChineseDay(), ]; return $chineseBirthday; } // 计算天干地支 function getTianGanDiZhi($chineseValue) { // 天干 $tianGan = ['甲', '', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']; // 地支 $diZhi = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']; // 计算天干地支 $index = ($chineseValue - 4) % 60; $tianGanIndex = $index % 10; $diZhiIndex = $index % 12; $tianGanValue = $tianGan[$tianGanIndex]; $diZhiValue = $diZhi[$diZhiIndex]; return [$tianGanValue, $diZhiValue]; } // 查找八字神煞 function getShenSha($yearTianGan, $yearDiZhi, $monthTianGan, $monthDiZhi, $dayTianGan, $dayDiZhi) { // 八字神煞表 $shenShaTable = [ '甲子' => ['天', '文昌'], '甲戌' => ['天厨', '文曲'], '丑' => ['吊客', '天哭'], '酉' => ['陀罗', '天虚'], '丙寅' => ['将星', '天月'], '丙申' => ['天巫', '天德'], '丁卯' => ['天才', '天福'], '丁酉' => ['天寿', '天恩'], '戊辰' => ['天贵', '天使'], '戊戌' => ['天荫', '天罡'], '己巳' => ['天福', '天官'], '己亥' => ['天伤', '天蓬'], '庚午' => ['天空', '天任'], '庚子' => ['天后', '天伯'], '辛未' => ['天印', '天威'], '辛酉' => ['天权', '天禄'], '壬申' => ['天德', '天'], '壬子' => ['天才', '天英'], '癸未' => ['天寿', '天巫'], '癸酉' => ['天恩', '天贵'], ]; // 查找八字神煞 $shenSha = []; $key = $yearTianGan . $yearDiZhi; if (isset($shenShaTable[$key])) { $shenSha = array_merge($shenSha, $shenShaTable[$key]); } $key = $monthTianGan . $monthDiZhi; if (isset($shenShaTable[$key])) { $shenSha = array_merge($shenSha, $shenShaTable[$key]); } $key = $dayTianGan . $dayDiZhi; if (isset($shenShaTable[$key])) { $shenSha = array_merge($shenSha, $shenShaTable[$key]); } return $shenSha; } ``` 需要注意的是,以上代码示例中使用了第三方库 `ChineseCalendar` 来实现阳历和农历的转换,使用前需要先安装该库。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值