PHP:: Construindo uma classe para Calendário

Calendário

CLASSE PARA CALENDÁRIO EM PHP

Nível Intermediário.

Tenho certeza que muita gente têm a necessidade de encontrar uma classe calendário ou mesmo construí-la. Talvez você até já tenha construído um calendário mas, por algum motivo não ficou plenamente satisfeito com ele. Certo, então, vamos construir algo que lhe dê boa base para criar ou enriquecer o que já possui. Este artigo procura destrinchar em detalhes cada coisa pensada para se criar as classes.

Este artigo designa o nível como intermediário porque espera do leitor que tenha boas noções de programação orientada a objetos. Entretanto, é obvio que mesmo se você for iniciante mas tiver boa noção de programação orientada a objetos, poderá tirar muito proveito deste artigo. O que quero dizer de forma mais específica é, que não pretendo entrar em detalhes sobre momentos em que você deve abrir ou fechar tags do php pelos smbolos “<?php” e “?>” entre outras coisas simples assim.

Além disso, você é livre para decidir se criará as classes descritas aqui, em arquivos separados ou em um único arquivo com extensão .php com o nome de sua preferência. Mas, é claro que a recomendação de em um servidor de produção, sempre utilizar cada classe em um arquivo diferente, separado do código cliente e organizados em pacotes, continua! Além disso, IDE também é sua escolha, pois você pode preferir desde um simples editor de textos até uma poderosa IDE com alto investimento $$$, use aquela que você está acostumado! 😉

Análise inicial

Precisamos primeiramente pensar em um calendário como um objeto e que características possui este objeto. Quando você olha para um calendário, está interessado em ver o quê? Vamos separar umas ideias:

  • Que dia da semana é hoje? Segunda-feira, então que dia do mês?
  • Que dia da semana irá cair no próximo sábado?
  • Este mês vai até dia 31?
  • Quantas semanas têm este mês?
  • Têm feriado este mês?

Além disso, você pode querer avançar as folhas de um calendário para olhar o mês seguinte ou mesmo, retroceder umas folhas para olhar um mês anterior. Talvez, até dar uma olhada em algum dia de algum mês no próximo ano.

Mas, quando falamos de um calendário em uma aplicação, para que ele servirá? Porque queremos um calendário em uma aplicação?

  • Talvez, será que tornará mais fácil a interface com o usuário ao preencher um campo de data?
  • Ou será que poderá tornar nosso blog mais atraente, permitindo ao visitante localizar artigos de uma data específica?
  • Ou ainda, será que precisamos criar algum tipo de sistema de agenda de compromissos em que um calendário possibilitará uma melhor navegação?

Pensamos em muitas coisas! Então, podemos imaginar alguns atributos iniciais que não poderiam faltar em nossa classe:

  • atributo para mês do calendário
  • atributo para ano do calendário
  • atributo para guardar todos os dias do Mês
  • atributo para nome dos dias da semana por extenso
  • atributo para sigla dos nomes dos dias da semana, ou abreviação
  • atributo para nomes dos meses do ano por extenso

Além disso vamos precisar de métodos para manipular o calendário, e talvez possamos pensar em alguns assim:

Para criação:

  • Crie um calendário conforme mês e ano informado

Para exibição:

  • Mostre na tela os dias do mês e ano conforme calendário criado
  • Devolva a data do calendário no formato do banco de dados
  • Devolva a data do calendário no formato de ancora parar URL

Sim, você pode ter pensado em outros, mas para começarmos, estes já permitem fazer algo funcional e estabelecer alguns requisitos.

Primeira etapa – Criando a uma classe abstrata base

No arquivo php que você criou, abra as tags do php “<?php” e “?>” logo no início, pulando algumas linhas entre elas. Então, a fim de seguir os requisitos que levantamos, vamos criar a estrutura da classe, inserindo o seguinte código:

<?php

abstract class MeuCalendario
{

   private $ano;
   private $mes;
   private $diasDoMes;
   private $diasDoMesAnterior;
   private $diasDoMesSeguinte;
   private $siglasDiasSemana = array("Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb");
   private $nomesMesesAno = array("Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro");

   public function __construct($mes, $ano)
   {
       $this->mes = $mes;
       $this->ano = $ano;
   }

   public abstract function getCalendario();
}

?>

Até aqui, temos uma classe abstrata com um construtor que recebe o mês e o ano como argumentos do tipo inteiro, para finalmente construir o calendário. Porém, nossa classe não está muito organizada e segura, pois ainda permite entrada de dados inválido, o que poderá causar falhas na construção do objeto. Então, vamos incluir validações que vão verificar se os valores são apenas numéricos e inteiros, dentro de uma faixa específica para não comprometer os cálculos em cima do timestamp. Esse timestamp é um inteiro longo contendo o número de segundos a partir de 1 de Janeiro de 1970 00:00:00. O timestamp foi desenvolvido nos sistemas Unix inicialmente nesta época em que ele faz a base de sua contagem. Assim, como você talvez já saiba, o php utiliza este mesmo formato para trabalhar com cálculos de tempo. Assim, o php possui uma série de funções nativas que trabalham com timestamp e faremos uso de algumas delas.

Vamos mudar nosso construtor na classe para o seguinte código:

 public function __construct($mes, $ano)
 {
     if (is_int($mes) && $mes <= 12 || $mes >= 1) {
         $this->mes = $mes;
     } else {
         throw new Exception("Erro: o valor definido para mês não é válido: ".$mes);
     }
     
    if (is_int($ano) && $ano >= 1970 || $ano <= 2099) {
         $this->ano = $ano;
     } else {
         throw new Exception("Erro: o valor definido para ano não é válido: ".$ano);
     }
 }

Agora o construtor está mais seguro! é claro que existem outras maneiras de fazer a validação, mas esta já atenderá bem nossa necessidade.

A classe MeuCalendário não poderá ser instanciada por ser abstrata, e mesmo se pudesse não teria utilidade nenhuma em si para o código cliente e você vai entender melhor o porquê. Ainda mais neste momento em que só contamos com o construtor que recebe dois argumentos e associa-os ao seus atributos. Outro detalhe é, até este momento, incluímos apenas um método abstrato, MeuCalendario::getCalendario(), que é importantíssimo e que deverá ser implementado por qualquer subclasses que estendam esta classe MeuCalendario.

Esteja ciente que vamos construir uma série de métodos, então, é sempre importante lembrar-se que métodos, assim como classes, precisam ter uma única responsabilidade. Vamos nos esforçar aqui a manter esta boa prática de programação orientada a objetos.

Para implementar funcionalidades, precisamos pensar sobre o que acontecerá quando olharmos para o calendário. Algo que sabemos sobre nosso calendário padrão é que quase sempre em suas folhas, aparecem alguns dias do mês anterior. Isto é obvio porque nem sempre o primeiro dia do mês irá cair em um Domingo, dia que se inicia a semana no calendário. Por isso, atento a esse detalhe e a fim de deixar nosso calendário mais completo, vamos adicionar métodos para dar suporte a isso. Vamos incluir dois métodos que ajudarão na no processo de construção do calendário:

Para verificar se aparacem dias do mês anterior, usaremos o método MeuCalendario::isDiasMesAnterior(). Portanto, adicione-o a classe:

protected function isDiasMesAnterior()
{
    return date('w', mktime(0, 0, 0, $this->mes, 1, $this->ano)) > 0 ? TRUE : FALSE;
}

E também para verificar se aparecem dias do mês seguinte, usaremos o método MeuCalendario::isDiasMesSeguinte(). Então, assim como no caso anterior, adicione-o a classe:

protected function isDiasMesSeguinte()
{
    return date('w', strtotime("last day of this month", mktime(0, 0, 0, $this->mes, 1, $this->ano))) <= 6 ? TRUE : FALSE;
}

Note aqui em ambos os métodos MeuCalendario::isDiasMesAnterior() e MeuCalendario::isDiasMesSeguinte(), que utilizamos a função mktime() do php para converter os dados informados como mês, dia e ano para um número timestamp.

Tendo o timestamp, podemos fazer uso dele tando na função date(), quanto na função strtotime(). No primeiro método utilizamos apenas a função date() do php. Esta função converte e formata uma data que está no modo timestamp para um modo que especificarmos no primeiro parâmetro. Já no segundo método, fizemos uso da função strtotime(). A função strtotime() é interessante porque nos permite fazer certos cálculos de datas, informando numa string o modo como queremos. Isto mesmo! Mas é claro, a função apenas compreende as palavras na String que estejam em idioma inglês. Então, será aplicado o calculo e em seguida, convertido novamente para timestamp. Por isso, o resultado dela já é passado como argumento para uma função date, que verificará para nós que dia da semana (ex: segunda-feira, terça-feira, etc …) cai o dia primeiro do mês informado. Quando temos métodos como este, podemos já preparar alguns dados, como quais são os dias do do mês seguinte que aparecem na folha do calendário e, quais são os dias do mês anterior que ainda aparecem na folha do calendário. Os seguintes métodos, irão verificar isso e guardar em um atributo da superclasse MeuCalendario:

Este método MeuCalendario::setDiasMesAnterior() define quais são os dias do mês anterior que ainda deverão ser impresso na folha do calendário:

 private function setDiasDoMesAnterior()
 {
     $diaNaSemana = date('w', strtotime("{$this->ano}-{$this->mes}-01 - 1 day"));
     $dia = date('d', strtotime("{$this->ano}-{$this->mes}-01 - 1 day")) - $diaNaSemana;
     
      // Preenche array
     for ($diaSemana = 0; $diaSemana <= $diaNaSemana; $diaSemana++) {
         $arrayDias[] = (int) $dia++;
     }
     return $arrayDias;
 }

Já este método MeuCalendario::setDiasMesSeguinte(), define quais são os dias do próximo mês, que já aparecem na folha do calendário:

 private function setDiasMesSeguinte()
 {
     $diaNaSemana = date('w', strtotime("+1 month", mktime(0, 0, 0, $this->mes, 1, $this->ano)));
     $dia = 1;
 
     // Preenche array
     for ($diaSemana = $diaNaSemana; $diaSemana <= 6; $diaSemana++) {
         $arrayDias[] = (int) $dia++;
     }

     // Adicionar mais uma semana para evitar perder a quinta semana de alguns meses
     for ($add = 0; $add <= 6; $add++) {
         $arrayDias[] = (int) $dia++;
     }

     return $arrayDias;
 }

Os métodos acima MeuCalendario::setDiasMesAnterior() e MeuCalendario::setDiasMesSeguinte(), não utilizam nenhuma função diferente das que já haviam sendo abrangidas MeuCalendario::isDiasMesAnterior() e MeuCalendario::isDiasMesSeguinte(). As funções são as mesmas e foram reorganizadas para exercer um papel diferente. Note um detalhe em que um Loop For vai preenchendo a array $arrayDias, apenas com dias que precisamos que ele faça. Após criarmos os métodos MeuCalendario::setDiasMesAnterior() e MeuCalendario::setDiasMesSeguinte(), que fazem este belo arranjo inicial, já é possivel implementar o método MeuCalendario::setDiasCalendario(), que define enfim, quais são os dias que aparecerão na folha do calendário:

private function setDiasCalendario()
{
    // Descobre qual o ultimo dia do mês informado
    $ultimoDiaMes = date('t', mktime(0, 0, 0, $this->mes, 1, $this->ano));
    
    // Preenche array
    for ($dia = 1; $dia <= $ultimoDiaMes; $dia++) {
        $arrayDias[] = (int) $dia;
    }

    // Adicionar os dias do mês anterior, se houver
    if ($this->isDiasMesAnterior()) {
        $arrayDias = array_merge($this->getDiasDoMesAnterior(), $arrayDias);
    }

    // Adicionar os dias do mês seguinte, se houver
    if ($this->isDiasMesSeguinte()) {
        $arrayDias = array_merge($arrayDias, $this->getDiasDoMesSeguinte());
    }

    // Evitar falhas para meses que terminam no dia de sábado com dia 30
    if(count($arrayDias) < 41){
        for($dia = 1; $dia <= 7; $dia++){
            $arrayDias[] = (int) $dia;
        }
    }
    return $arrayDias;
}

Este método faz uma junção de todos os números resgatados e guardados em arrays diferentes, para uma única array que será a base de construção do calendário. Mas para tudo isso funcionar depois que fizermos a chamado no local certo, ainda precisamos incluir dois métodos para obter o números guardados nos outros atributos:

/**
 * Retorna um array com os dias do mês anterior que ainda aparecem na folha do calendário
 * @return array
 */

 private function getDiasDoMesAnterior()
 {
     return $this->diasDoMesAnterior;
 }

/**
 * Retorna um array com os dias do mês seguinte que já aparecem na folha do calendário
 * @return array
 */

 private function getDiasDoMesSeguinte()
 {
     return $this->diasDoMesSeguinte;
 }

Então, daí sim, para tudo isso começar a funcionar e não depender de intervenção do usuário, precisamos incluir as chamadas a estes métodos no construtor da classe MeuCalendário. Veja como fica agora o construtor da classe:

public function __construct($mes, $ano)

{

    if (is_int($mes) && $mes <= 12 || $mes >= 1) {
        $this->mes = $mes;
    } else {
       throw new Exception("Erro: o valor definido para mês não é válido: ".$mes);
    }

   if (is_int($ano) && $ano >= 1970 || $ano <= 2099) {
       $this->ano = $ano;
   } else {
       throw new Exception("Erro: o valor definido para ano não é válido: ".$ano);
   }
  
   // Primeiro verificar se existem dias do mês anterior que aparecem na folha do calendário
   if ($this->isDiasMesAnterior()) {
       $this->diasDoMesAnterior = $this->setDiasDoMesAnterior();
   }

   // Também verificar se existem dias do mês seguinte que aparecem na folha do calendário
   if ($this->isDiasMesSeguinte()) {
       $this->diasDoMesSeguinte = $this->setDiasMesSeguinte();
   }

   // Chamar método que monta os dias na folha do calendário
   $this->diasDoMes = $this->setDiasCalendario();
}

Você notou alí no construtor, como fazemos uso de todos os métodos criados até este momento? Criamos os métodos que serão base para estruturas de decisão e métodos para ação após decisão. Por isso, não são métodos públicos. Serão utilizados apenas pelas classes envolvidas, sendo que, os métodos utilizados especificamente para construção, não daremos visibilidade nem para as classes herdadas. Isso protege contra decisões precipitadas de quem for utilizar a classe.

Já que implementamos toda parte de lógica de construção de objetos MeuCalendario, precisamos dar suporte as classes que vão herdar e implementar métodos que serão utilizados pelo usuário final da classe. Assim, queremos que esta classe possua tudo que permita gerar heranças consistentes. Por isso, vamos implementar métodos que dão suporte a herança de forma prática e segura:

/**
 * Retorna a sigla da semana conforme um número de indide informado
 * @param int $diaSemana
 * @return String "Sigla do dia da semana. Ex: Seg, Ter, Qua..."
 * @throws Exception
 */
 protected function getSiglaPorDia($diaSemana)
 {
     if (is_int($diaSemana) && $diaSemana <= 6 || $diaSemana >= 0) {
         return $this->siglasDiasSemana[$diaSemana];
     } else {
         throw new Exception("Erro: Valor numérico invalido para se obter nome do dia da semana");
     }
 }

/**
 * Retorna o nome do Mês corrente por extenso
 * @return string
 */
 protected function getNomeMes()
 {
     return $this->nomesMesesAno[$this->mes - 1];
 }

/**
 * Retorna um array comos dias definidos para folha do calendário
 * @return array
 */
 protected function getDiasDoMes()
 {
     return $this->diasDoMes;
 }

/**
 * Retorna o mês no formato numérico
 * @return int
 */
 protected function getMes()
 {
     return $this->mes;
 }

/**
 * Retorna o ano no formato numérico
 * @return int
 */
 protected function getAno()
 {
     return $this->ano;
 }

Percebeu um detalhe importantíssimo alí nestes últimos cinco métodos? Todos possuem visibilidade apenas nas heranças, ou seja, não deverão ser usados pelo usuário que fará uso da classe herdada. Apenas queremos dar o suporte suficiente a herança. Neste caso, ficará delegado as subclasses cuidarem da interação com a aplicação cliente.

Então temos a nossa classe abstrata já preparada para ser herdada por uma classe que poderá implementar a funcionalidade que precisamos. O objetivo desta superclasse é criar a estrutura do calendário e permitir ser manipulado. Ela não elabora um método de apresentar o calendário ao usuário por imprimir na tela. Muito menos, deve interagir com o código cliente. Isto será feito pelas subclasses que herdarem de MeuCalendario. Note como a classe MeuCalendario é na integra:

/**
 * Classe abstrata MeuCalendario
 * 
 * @abstract class
 */
abstract class MeuCalendario
{
     private $ano;
     private $mes;
     private $diasDoMes;
     private $diasDoMesAnterior;
     private $diasDoMesSeguinte;
     private $siglasDiasSemana = array("Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb");
     private $nomesMesesAno = array("Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro");

     public function __construct($mes, $ano)
     {
         if (is_int($mes) && $mes <= 12 || $mes >= 1) {
             $this->mes = $mes;
         } else {
             throw new Exception("Erro: o valor definido para mês não é válido: ".$mes);
         }

         if (is_int($ano) && $ano >= 1970 || $ano <= 2099) {
             $this->ano = $ano;
         } else {
             throw new Exception("Erro: o valor definido para ano não é válido: ".$ano);
         }

         // Primeiro verificar se existem dias do mês anterior que aparecem na folha do calendário
         if ($this->isDiasMesAnterior()) {
             $this->diasDoMesAnterior = $this->setDiasDoMesAnterior();
         }

         // Também verificar se existem dias do mês seguinte que aparecem na folha do calendário
         if ($this->isDiasMesSeguinte()) {
             $this->diasDoMesSeguinte = $this->setDiasMesSeguinte();
         }

         // Chamar método que monta os dias na folha do calendário
         $this->diasDoMes = $this->setDiasCalendario();
     }

    /**
     * Verifica se há dias do mês anterior no calendário
     * @return boolean
     */
     protected function isDiasMesAnterior()
     {
         return date('w', mktime(0, 0, 0, $this->mes, 1, $this->ano)) > 0 ? TRUE : FALSE;
     }

    /**
     * Verifica se há dias do mês seguinte no calendário
     * @return boolean
     */
     protected function isDiasMesSeguinte()
     {
         return date('w', strtotime("last day of this month", mktime(0, 0, 0, $this->mes, 1, $this->ano))) <= 6 ? TRUE : FALSE;
     }

    /**
     * Retorna um array contendo os dias do mês anterior que aparecem na folha do calendário
     * @return array
     */
     private function setDiasDoMesAnterior()
     {
         $diaNaSemana = date('w', strtotime("{$this->ano}-{$this->mes}-01 - 1 day"));
         $dia = date('d', strtotime("{$this->ano}-{$this->mes}-01 - 1 day")) - $diaNaSemana;

         // Preenche array
         for ($diaSemana = 0; $diaSemana <= $diaNaSemana; $diaSemana++) {
             $arrayDias[] = (int) $dia++;
         }

         return $arrayDias;
     }

    /**
     * Retorna um array contendo os dias do mês seguinte que aparecem na folha do calendário
     * @return array
     */
     private function setDiasMesSeguinte()
     {
         $diaNaSemana = date('w', strtotime("+1 month", mktime(0, 0, 0, $this->mes, 1, $this->ano)));
         $dia = 1;

         // Preenche array
         for ($diaSemana = $diaNaSemana; $diaSemana <= 6; $diaSemana++) {
             $arrayDias[] = (int) $dia++;
         }

         // Adicionar mais uma semana para evitar perder a quinta semana de alguns meses
         for ($add = 0; $add <= 6; $add++) {
            $arrayDias[] = (int) $dia++;
         }

         // Adicionar mais uma semana para evitar perder a quinta semana de alguns meses
         for ($add = 0; $add <= 6; $add++) {
             $arrayDias[] = (int) $dia++;
         }

         return $arrayDias;
     }

     private function setDiasCalendario()
     {
         // Descobre qual o ultimo dia do mês informado
         $ultimoDiaMes = date('t', mktime(0, 0, 0, $this->mes, 1, $this->ano));

         // Preenche array
         for ($dia = 1; $dia <= $ultimoDiaMes; $dia++) {
             $arrayDias[] = (int) $dia;
         }

         // Adicionar os dias do mês anterior, se houver
         if ($this->isDiasMesAnterior()) {
             $arrayDias = array_merge($this->getDiasDoMesAnterior(), $arrayDias);
         }

         // Adicionar os dias do mês seguinte, se houver
         if ($this->isDiasMesSeguinte()) {
             $arrayDias = array_merge($arrayDias, $this->getDiasDoMesSeguinte());
         }

         // Evitar falhas para meses que terminam no dia de sábado com dia 30
         if(count($arrayDias) < 41){
             for($dia = 1; $dia <= 7; $dia++){
                 $arrayDias[] = (int) $dia;
             }
         }

         return $arrayDias;
     }

    /**
     * Retorna um array com os dias do mês anterior que ainda aparecem na folha do calendário
     * @return array
     */
     private function getDiasDoMesAnterior()
     {
         return $this->diasDoMesAnterior;
     }

    /**
     * Retorna um array com os dias do mês seguinte que já aparecem na folha do calendário
     * @return array
     */
     private function getDiasDoMesSeguinte()
     {
         return $this->diasDoMesSeguinte;
     }

    /**
     * Retorna a sigla da semana conforme um número de indide informado
     * @param int $diaSemana
     * @return String "Sigla do dia da semana. Ex: Seg, Ter, Qua..."
     * @throws Exception
     */
     protected function getSiglaPorDia($diaSemana)
     {
         if (is_int($diaSemana) && $diaSemana <= 6 || $diaSemana >= 0) {
             return $this->siglasDiasSemana[$diaSemana];
         } else {
             throw new Exception("Erro: Valor numérico invalido para se obter nome do dia da semana");
         }
     }

    /**
     * Retorna o nome do Mês corrente por extenso
     * @return string
     */
     protected function getNomeMes()
     {
         return $this->nomesMesesAno[$this->mes - 1];
     }

    /**
     * Retorna um array comos dias definidos para folha do calendário
     * @return array
     */
     protected function getDiasDoMes()
     {
         return $this->diasDoMes;
     }

    /**
     * Retorna o mês no formato numérico
     * @return int
     */
     protected function getMes()
     {
         return $this->mes;
     }

    /**
     * Retorna o ano no formato numérico
     * @return int
     */

     protected function getAno()
     {
         return $this->ano;
     }

     public abstract function getCalendario();
}

Segunda etapa – Criando uma classe que herda da classe abstrata e implementa a impressão

Certo, mas esta classe que terminamos agora, como já sabemos não vai gerar objeto diretamente. Então precisamos criar uma classe que estenda MeuCalendario e implemente toda funcionalidade necessária. Esta é a classe MeuCalendarioHTML! perceba que o construtor apenas repassa os argumentos para o construtor da superclasse:

class MeuCalendarioHTML extends MeuCalendario
{
     public function __construct($mes, $ano)
     {
         parent::__construct($mes, $ano);
     }
}

Tecnicamente, esta subclasse MeuCalendarioHTML já pode ser instanciada mas, não será util ainda neste momento. Por esta maneira, para construirmos métodos que apresentarão resultados visíveis na tela, precisamos antes criar alguns métodos que dão suporte a visibilidade. Os primeiros dois métodos MeuCalendarioHTML::isAnterior() e MeuCalendarioHTML::isSeguinte() são aqueles que vão informar a classe se há dias para imprimir que não fazem parte do mês corrente, ou seja, os dias do mês anterior e posterior, mas que aparecem na folha do calendário.

O método MeuCalendarioHTML::isAnterior() retorna um valor booleano que será utilizado para facilitar a estrutura de decisão mais adiante dento do método MeuCalendarioHTML::getCalendario(). O que espera-se, é utilização deste método na iteração do array da superclasse que contem todos os dias da folha do calendário. Isto ficará mais claro quando chegar lá no método MeuCalendarioHTML::getCalendario(), e você perceber que o método MeuCalendarioHTML::isAnterior() é usado dentro de um bloco if para testar os números da iteração e descobrir, se fazem parte do mês corrente ou ainda são do mês anterior. Note que ele têm suporte do método MeuCalendario::isDiasMesAnterior() da superclasse:

/**
 * Ajuda a decidir se irá imprimir os dias do mês anterior
 * @param int $indexLoop
 * @return boolean
 */
 private function isAnterior($indexLoop)
 {
     if (is_int($indexLoop)) {
         return (($this->isDiasMesAnterior() == TRUE) && ($this->getDiasDoMes()[$indexLoop] > 25 && $indexLoop < 6)) ? TRUE : FALSE;
     } else {
         throw new Exception("Erro: Valor incorreto indicado como indice de array do calendário: ".$indexLoop);
     }
 }

Da mesma forma, este outro método MeuCalendarioHTML::isSeguinte(), também tem o objetivo de facilitar a estrutura de decisão, sendo usado no bloco if no método MeuCalendarioHTML::getCalendario() para testar os números da iteração e descobrir se são referentes a dias do mês corrente ou já fazem parte do mês posterior. Ele também faz uso do método MeuCalendario::isDiasMesSeguinte() da superclasse:

/**
 * Ajuda a decidir se irá imprimir os dias do mês seguinte
 * @param int $indexLoop
 * @return boolean
 */
 private function isSeguinte($indexLoop)
 {
     if (is_int($indexLoop)) {
         return (($this->isDiasMesSeguinte() == TRUE) && ($this->getDiasDoMes()[$indexLoop] < 15 && $indexLoop > 27)) ? TRUE : FALSE;
     } else {
         throw new Exception("Erro: Valor incorreto indicado como indice de array do calendário: ".$indexLoop);
     }
 }

Você deve incluir estes métodos na classe MeuCalendárioHTML e aí sim um muito bem! Damos um grande passo!

Mas nosso calendário html não servirá para nada se não permitir nenhum tipo interação com o usuário ou o código cliente. Então, pensando nisso, vamos adicionar algumas possibilidades de decisões durante a navegação através de links. Já que isso envolve a criação de links dinamicamente conforme o mês corrente, precisamos contar com métodos que geram estes links dinamicamente. Nos quatro métodos seguintes, é feito uso da função do php strtotime(), onde informamos através de uma String com palavras em inglês o que se deseja fazer. O operação sempre é realizada em cima do timestamp obtido pela função mktime() que está no segundo argumento de strtotime(). O timestamp gerado pela função strtotime() é passado como argumento para função date() que dará um formato, conforme especificado no primeiro argumento “em formato de String” (delimitado por aspas). O resultado obtido em date() é passado como segundo argumento para a função explode() a fim de converter a mesma em um array. Este array é usado na segunda para criar a String de link que será retornada ao método que faz o chamado. Os quatro métodos seguintes, que você precisa adicionar a classe obtido pelo método MeuCalendarioHTML, funcionam da mesma forma:

1- Um link para retroceder ao mês anterior obtido pelo método MeuCalendarioHTML::getLinkMesAnterior():

/**
 * Link para mês anterior
 * @return String
 */
 private function getLinkMesAnterior()
 {
     $data = explode("/", date("m/Y", strtotime("-1 month", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
     return "?m={$data[0]}&a={$data[1]}";
 }

2- Um link para avançar ao mês seguinte obtido pelo método MeuCalendarioHTML::getLinkMesSeguinte()::

/**
 * Link para mês seguinte
 * @return String
 */
 private function getLinkMesSeguinte()
 {
     $data = explode("/", date("m/Y", strtotime("+1 month", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
     return "?m={$data[0]}&a={$data[1]}";
 }

3- Um link para retroceder ao ano anterior obtido pelo método MeuCalendarioHTML::getLinkAnoAnterior()::

/**
 * Link para ano anterior
 * @return String
 */
 private function getLinkAnoAnterior()
 {
     $data = explode("/", date("m/Y", strtotime("-1 year", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
     return "?m={$data[0]}&a={$data[1]}";
 }

4- Um link para avançarr ao ano seguinte obtido pelo método MeuCalendarioHTML::getLinkAnoSeguinte()::

/**
 * Link para ano seguinte
 * @return String
 */
 private function getLinkAnoSeguinte()
 {
     $data = explode("/", date("m/Y", strtotime("+1 year", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
     return "?m={$data[0]}&a={$data[1]}";
 }

Agora nossa subclasse MeuCalendarioHTML está quase concluída. Falta implementar o método MeuCalendarioHTML::getCalendario(), que permitirá imprimir um calendário na tela. Naturalmente, você pode criar outros tipos de implementação para o método MeuCalendarioHTML::getCalendario(), mas o código apresentado a seguir, utiliza as tags htm table para organizar os números de dias e informações. Veja que neste momento não vamos nos preocupar sobre conceitos de de tableless & cia … 😉 

Esta implementação de MeuCalendarioHTML::getCalendario() é muito simples, e começa por montar uma String na variável $str. Aqui se faz uso de duas estruturas de “loops de repetição for” para primeiro iterar sobre os dias da semana em um formato compatível ao de date(‘w’). Este formato escala os dias da semana iniciando em domingo e terminado em sábado mas de forma numérica, iniciando em 0 e terminando em 6. Veja que são feito chamadas aos métodos da superclasse MeuCalendario::getSiglaPorDia( int $dia), para se obter a sigla ou abreviação do nome do dia da semana conforme argumento do tipo inteiro para dia, e MeuCalendario::getDiasDoMes(), para recuperar o array contendo todos os dias da folha do calendário. Ambos estão sendo chamados dentro de laços de repetição for. E assim é possível montar o cabeçalho do calendário. O método MeuCalendario::getDiasDoMes(), se refere a uma array com 35 indices e em cada posição, um dia do calendário que é aplicada ao placeholder da string $str a cada iteração do laço for. Note que há dois blocos if e um bloco eles. Nestes blocos é feito uma distinção sobre como imprimir os dias. A decisão é tomada com base na resposta obtida nos métodos MeuCalendarioHTML::isAnterior(int $indice) e MeuCalendarioHTML::isSeguinte(int $indice). Ambos os métodos retornaram um valor booleano após testar se o número corrente na iteração se refere a um indice no array que representa dias do mês anterior ou seguinte. São apenas três posibilidades, sendo que, o bloco eles imprimirá os dias referente ao mês corrente, informado na construção do objeto. Por isso, em cima desta impressão são formado links de ancoras para interagir com o código cliente via $_GET ou $_REQUEST. Finalmente, após concluir a montagem da String na variável $str, ela é retornada e pode ser impressa pelo código cliente. Note o método MeuCalendarioHTML::getCalendario(), insira ele na classe MeuCalendarioHTML e faça o tão esperado teste!

/**
 * Retorna String html do Calendário
 * @return string Calendário HTML
 */
 public function getCalendario()
 {
     $str = "<table border=\"1\">\n\r";
     $str .= "\t<tr>\n\r\t\t<td colspan=\"7\"><center>\n\r";
     $str .= "\t\t<a href=\"{$this->getLinkMesAnterior()}\"><</a> {$this->getNomeMes()} ";
     $str .= "<a href=\"{$this->getLinkMesSeguinte()}\">></a> ";
     $str .= "de <a href=\"{$this->getLinkAnoAnterior()}\"><</a> {$this->getAno()} ";
     $str .= "<a href=\"{$this->getLinkAnoSeguinte()}\">></a> \n\r\t\t</center></td>\n\r\t<tr>\n\r";
     for ($index = 0; $index <= 6; $index++) {
         $str .= "\t\t<th>{$this->getSiglaPorDia($index)}</th>\n\r";
     }
 
     for ($index = 0; $index <= 34; $index++) {
         // Cor da célula
         $cor = ($index % 7 == 0)? "#D3D3D3" : "#F5F5F5";
         if ($index % 7 == 0) $str .= "\t</tr><tr>\n\r";
             if ($this->isAnterior($index)) {
                 $str .= "\t\t<td bgcolor=\"{$cor}\"><i>{$this->getDiasDoMes()[$index]}</i></td>\n\r";
             } else if ($this->isSeguinte($index)) {
                 $str .= "\t\t<td bgcolor=\"{$cor}\"><i>{$this->getDiasDoMes()[$index]}</i></td>\n\r";
             } else {
                 $dia = $this->getDiasDoMes()[$index];
                 $str .= "\t\t<td bgcolor=\"{$cor}\"><a href=\"?d={$dia}&m={$this->getMes()}&a={$this->getAno()}\">{$dia}</a></td>\n\r";
             }
         }
         $str .= "\t<tr>\n\r\t</table>\n\r";
         return $str;
     }

Concluíndo com este método abstrato que faltava implementar, temos a classe MeuCalendarioHTML desta maneira:

/**
 * Classe MeuCalendarioHTML
 *
 * @author Alexandre Bezerra Barbosa <alxbbarbosa@yahoo.com.br>
 */
 class MeuCalendarioHTML extends MeuCalendario
 {
     public function __construct($mes, $ano)
     {
         parent::__construct($mes, $ano);
     }

    /**
     * Ajuda a decidir se irá imprimir os dias do mês anterior
     * @param int $indexLoop
     * @return boolean
     */
     private function isAnterior($indexLoop)
     {
         if (is_int($indexLoop)) {
             return (($this->isDiasMesAnterior() == TRUE) && ($this->getDiasDoMes()[$indexLoop] > 25 && $indexLoop < 6)) ? TRUE : FALSE;
         } else {
             throw new Exception("Erro: Valor incorreto indicado como indice de array do calendário: ".$indexLoop);
         }
     }

    /**
     * Ajuda a decidir se irá imprimir os dias do mês seguinte
     * @param int $indexLoop
     * @return boolean
     */
     private function isSeguinte($indexLoop)
     {
         if (is_int($indexLoop)) {
             return (($this->isDiasMesSeguinte() == TRUE) && ($this->getDiasDoMes()[$indexLoop] < 15 && $indexLoop > 27)) ? TRUE : FALSE;
         } else {
             throw new Exception("Erro: Valor incorreto indicado como indice de array do calendário: ".$indexLoop);
         }
     }

    /**
     * Link para mês anterior
     * @return String
     */
     private function getLinkMesAnterior()
     {
         $data = explode("/", date("m/Y", strtotime("-1 month", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Link para mês seguinte
     * @return String
     */
     private function getLinkMesSeguinte()
     {
         $data = explode("/", date("m/Y", strtotime("+1 month", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Link para ano anterior
     * @return String
     */
     private function getLinkAnoAnterior()
     {
         $data = explode("/", date("m/Y", strtotime("-1 year", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Link para ano seguinte
     * @return String
     */
     private function getLinkAnoSeguinte()
     {
         $data = explode("/", date("m/Y", strtotime("+1 year", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Retorna String html do Calendário
     * @return string Calendário HTML
     */
     public function getCalendario()
     {
         $str = "<table border=\"1\">\n\r";
         $str .= "\t<tr>\n\r\t\t<td colspan=\"7\"><center>\n\r";
         $str .= "\t\t<a href=\"{$this->getLinkMesAnterior()}\"><</a> {$this->getNomeMes()} ";
         $str .= "<a href=\"{$this->getLinkMesSeguinte()}\">></a> ";
         $str .= "de <a href=\"{$this->getLinkAnoAnterior()}\"><</a> {$this->getAno()} ";
         $str .= "<a href=\"{$this->getLinkAnoSeguinte()}\">></a> \n\r\t\t</center></td>\n\r\t<tr>\n\r";
         for ($index = 0; $index <= 6; $index++) {
             $str .= "\t\t<th>{$this->getSiglaPorDia($index)}</th>\n\r";
         }
         for ($index = 0; $index <= 41; $index++) {
             // Cor da célula
             $cor = ($index % 7 == 0)? "#D3D3D3" : "#F5F5F5";
             if ($index % 7 == 0) $str .= "\t</tr><tr>\n\r";
                 if ($this->isAnterior($index)) {
                     $str .= "\t\t<td bgcolor=\"{$cor}\"><i>{$this->getDiasDoMes()[$index]}</i></td>\n\r";
                 } else if ($this->isSeguinte($index)) {
                     $str .= "\t\t<td bgcolor=\"{$cor}\"><i>{$this->getDiasDoMes()[$index]}</i></td>\n\r";
                 } else {
                     $dia = $this->getDiasDoMes()[$index];
                     $str .= "\t\t<td bgcolor=\"{$cor}\"><a href=\"?d={$dia}&m={$this->getMes()}&a={$this->getAno()}\">{$dia}</a></td>\n\r";
                 }
             }
             $str .= "\t<tr>\n\r\t</table>\n\r";
             return $str;
         }
     
     }

Terceira etapa – Instanciando um objeto do tipo MeuCalendarioHTML

Como então geramos uma instância na tela? Dentro de tags HTML, você poderá aproveitar os tratamentos de erros da classe MeuCalendario, e inserir os códigos dentro de blocos try e catch:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
     <?php
     
       try {
 
           $calendario = new MeuCalendarioHTML(9, 2016);
           echo $calendario->getCalendario();

       } catch (Exception $erros) {
           echo $erros->getMessage();
       }
   ?>
   </body>
</html>

Note que associamos uma instancia de MeuCalendarioHTML a variável $calendario e então em seguida, podemos chamar o método MeuCalendarioHTML:: getCalendario(). Lembre-se que este método retorna uma String, que para ser impressa, necessitará do uso de uma das funções do php echo ou print.

Feito isto o resultado será como o da imagem:

Artigo classe PHP para calendário: Exibindo na tela
Artigo classe PHP para calendário: Exibindo na tela

Evidentemente, você pode melhorar a aparência do calendário impresso fazendo uso de css. Mas, terminamos por aqui? 😦

Não! 😮

Na introdução deste artigo tinha mais uma pergunta que merece ter resposta. E se o usuário olhar para o calendário e quiser saber a resposta para: Têm feriado este mês? 😀

Quarta etapa – Implementando feriados

Bem, este não é um tema fácil, porque temos feriados móveis e fixos! Você não encontra classes tão interessantes por aí sobre este recurso e por isso, que acho que você ao terminar deve clicar no “Curtir” e se se tornar um seguidor deste blog!! 😀 😀 😀

Vamos alistar alguns feriados, que aparecem no calendário convencional:

  • Confraternização Universal
  • Tiradentes,
  • Dia do Trabalhador
  • Dia das mães
  • Revolução Constitucionalista de 1932 – São Paulo
  • Dia dos pais
  • Dia da Independência
  • N. S. Aparecida
  • Todos os santos
  • Proclamação da república
  • Natal

Certo, mas há algumas datas móveis e que dependem da páscoa! 😮

  • Carnaval (Ponto Facultativo)
  • Quarta-feira de cinzas
  • Paixão de Cristo
  • Páscoa
  • Corpus Christ

E então como fazer?

Precisamos criar uma nova classe que será utilizada para criar um objeto de feriado, e este objeto dará suporte a classe MeuCalendarioHTML. Podemos iniciar a construção dela com três atributos:

class Feriados
{
   private $ano;
   private $mes;
   private $feriados;
}

Queremos que o objeto seja construído com base no mês e ano, portanto, a nossa classe precisa ter um método construtor com argumentos para setar o mês e o ano ao atributos:

 public function __construct($mes, $ano)
 {
     if (is_int($mes) && $mes <= 12 && $mes >= 1) {
         $this->mes = $mes;
     } else {
         throw new Exception("Erro: o valor definido para mês não é válido: ".$mes);
     }
     if (is_int($ano) && $ano >= 1970 && $ano <= 2099) {
         $this->ano = $ano;
    } else {
         throw new Exception("Erro: o valor definido para ano não é válido: ".$ano);
    }
 }

É nítido que este método construtor até aqui se assemelha muito ao método construtor da classe MeuCalendario. Vamos, dar uma pausa nesta etapa e pensar nos métodos que darão suporte a classe Feriados, a fim de permitir adicionar os dias de feriado móveis além dos dias de feriados fixo. E pensando nos feriados móveis, temos os dois dias comerciais que se alteram de ano para ano de forma bem sutil: Dia das mães e dia dos pais. Ambos caem no seguindo domingo do mês, mas é obvio que os domingos destes meses não vão cair sempre no mesmo dia do mês. Assim, adicione o seguinte método Feriado::getSegundoDomingo(int $mes) que fará este calculo:

/**
 * Retorna timestamp referente ao segundo domingo do mês
 * @return int
 */
 private function getSegundoDomingo($mes)
 {
     $timestamp = strtotime("-1 day", mktime(0, 0, 0, $mes, 1, $this->ano));
     $diaNaSemana = date('w', $timestamp);
     $dia = date('d', $timestamp) - $diaNaSemana;
     
     // Preenche array
     for ($diaSemana = 0; $diaSemana <= $diaNaSemana; $diaSemana++) {
         $arrayDias[$dia++] = $diaSemana;
     }

     for ($dia = 1; $dia < 14; $dia++) {
         $arrayDias[$dia] = date("w", mktime(0, 0, 0, $mes, $dia, $this->ano));
     }

     $c = 0;
     
     foreach ($arrayDias as $d => $w) {
         if ($w == 0 && $d < 20) $c++;

         if ($c == 2) return mktime(0, 0, 0, $mes, $d, $this->ano);

     }
 }

Este método Feriado::getSegundoDomingo(int $mes), é privado porque só tem a função de dar suporte a construção do objeto. Note que em sua estrutura foi usado a função já conhecida strtotime() para se calcular obter um timestamp, que é resultado da função mktime(), conforme uma String informada no primeiro argumento. O resultado de strtotime() será associado a variável $timestamp que permitirá obter o dia na semana e o número do dia, por utilizar a função date(). A técnica seguinte será criar um array temporário contendo os dias do mês como índice e dia da semana como valore nas posições. Então tendo criado a array com dias suficiente para poder se obter um segundo domingo, passamos este array para um laço foreach que irá iterar por todas as posições. Dentro deste laço, são feitos comparações para se contar quantos domingos (representados pelo valor 0 obtido por date(‘w’)) se encontra a cada iteração. Em seguida, uma nova comparação num bloco if, descobre se a contagem alcançou 2 e se sim, fácil! Só retornar um timestamp criado pela função mktime() que tem como argumento o mês solicitado, o ano informado no atributo para o objeto e o dia na iteração que é está associado a variável $d.

Agora a fim de nos auxiliar a popular o atributo array $feriados, vamos adicionar um novo método Feriado::setFeriado(int $timestamp, String $descricao):

/**
 * Configura um feriado para o mês
 * @param int $timestamp
 * @param String $descricao
 */
 private function setFeriado($timestamp, $descricao)
 {
     if (date("n", $timestamp) == $this->mes) {
         $this->feriados[date('j', $timestamp)] = $descricao;
     }
 }

O método Feriado::setFeriado(int $timestamp, String $descricao), recebe dois argumentos: um timestamp e uma descrição do que é o valor deste timestamp. Sua funcionalidade é simples, primeiro faz uma comparação do resultado obtido pela função date(‘n’) tendo com segundo argumento este timestamp, com o número associado ao atributo do objeto $mes. Isto apenas para ter certeza de que estamos recebendo um timestamp para aquele mês. Se sim, é feito a associação dos dados a uma posição no atributo array Feriado::$feriados, tendo como indice o dia e o valor da posição, a descrição que passamos como segundo argumento ao método Feriado::setFeriado(int $timestamp, String $descricao).

Finalmente podemos voltar nossa atenção aos demais feriados móveis, aqueles que se baseiam na data da páscoa. E a pascoa? Bem, o php nos ajuda muito nisso! Contamos com uma função nativa do php, a easter_date() que retorna um timestamp correspondente à meia-noite da Páscoa do ano que é informado como argumento. Logo, podemos adicionar esta função no método construtor da classe Feriados, para definir o dia da páscoa imediatamente e associá-lo a uma variável a fim de ser usada para base de cálculo:

$pascoa = easter_date($this->ano);

Mas e se queremos saber os feriados religiosos em relação ao dia de páscoa, precisamos entender como funciona a base de cálculo para o calendário convencional: A segunda-feira de Carnaval acontece sempre a 48 dias antes do dia de páscoa, sendo que a terça-feira naturalmente será 47 dias antes. Estes normalmente são declarados como ponto facultativo. Logo a quarta feira de cinzas segue sequencia um dia após o carnaval, então evidentemente se dá a 46 dias antes da páscoa. E a sexta-feira santa? Como está apenas a dois dias da páscoa, são menos 2 dias. Agora faltou saber “Corpus Christ”: 60 dias depois do dia de páscoa. Sabendo isso podemos fazer uso da função strtotime() do php para nos ajudar nesses cálculos com base em Strings informadas no primeiro argumento da função. O resultado obtido de strtotime() é passado como um argumento, junto com a descrição que desejamos para este timestamp, informada no segundo argumento do método Feriado::setFeriado(), que foi criado para a classe Feriados. Este método Feriado::setFeriado(), como já visto acima, fará a adição dos dados no atributo array Feriado::$feriados.

Ciente de tudo isso, podemos inserir estas linhas em nosso método construtor:

if ($mes >= 1 && $mes <= 5) {
   $pascoa = easter_date($this->ano); // Páscoa
   $this->setFeriado($pascoa, "Páscoa");
   $this->setFeriado(strtotime("-48 days", $pascoa), "Carnaval (Ponto-facultativo)");
   $this->setFeriado(strtotime("-47 days", $pascoa), "Carnaval (Ponto-facultativo)");
   $this->setFeriado(strtotime("-46 days", $pascoa), "Quarta-feira de cinzas");
   $this->setFeriado(strtotime("-2 days", $pascoa), "Sexta-feira Santa (Paixão de Cristo)");
   $this->setFeriado(strtotime("+2 days", $pascoa), "Corpus Christ");
   $this->setFeriado($this->getSegundoDomingo(5), "Dia das mães (Dia comercial)");
   $this->setFeriado(mktime(0, 0, 0, 1, 1, $this->ano), "Confraternização Universal");
   $this->setFeriado(mktime(0, 0, 0, 4, 21, $this->ano), "Tiradentes");
   $this->setFeriado(mktime(0, 0, 0, 5, 1, $this->ano), "Dia do trabalhador");
} else if ($mes == 7) {
   $this->setFeriado(mktime(0, 0, 0, 7, 9, $this->ano), "Revolução Constitucionalista de 1932");
} else if ($mes == 8) {
   $this->setFeriado($this->getSegundoDomingo(8), "Dia dos pais (Dia comercial)");
} else if ($mes == 9) {
   $this->setFeriado(mktime(0, 0, 0, 9, 7, $this->ano), "Dia da Independência");
} else if ($mes == 10) {
   $this->setFeriado(mktime(0, 0, 0, 10, 12, $this->ano), "Nossa Senhora Aparecida");
} else if ($mes == 11) {
   $this->setFeriado(mktime(0, 0, 0, 11, 2, $this->ano), "Todos os Santos");
   $this->setFeriado(mktime(0, 0, 0, 11, 15, $this->ano), "Proclamação da Republica");
} else if ($mes == 12) {
   $this->setFeriado(mktime(0, 0, 0, 12, 25, $this->ano), "Natal");
}

Veja o método construtor em sua integra:

public function __construct($mes, $ano)
{
    if (is_int($mes) && $mes <= 12 && $mes >= 1) {
        $this->mes = $mes;
   } else {
        throw new Exception("Erro: o valor definido para mês não é válido: ".$mes);
   }

   if (is_int($ano) && $ano >= 1970 && $ano <= 2099) {
        $this->ano = $ano;
   } else {
        throw new Exception("Erro: o valor definido para ano não é válido: ".$ano);
   }

   if ($mes >= 1 && $mes <= 5) {
        $pascoa = easter_date($this->ano); // Páscoa
        $this->setFeriado($pascoa, "Páscoa");
        $this->setFeriado(strtotime("-48 days", $pascoa), "Carnaval (Ponto-facultativo)");
        $this->setFeriado(strtotime("-47 days", $pascoa), "Carnaval (Ponto-facultativo)");
        $this->setFeriado(strtotime("-46 days", $pascoa), "Quarta-feira de cinzas");
        $this->setFeriado(strtotime("-2 days", $pascoa), "Sexta-feira Santa (Paixão de Cristo)");
        $this->setFeriado(strtotime("+2 days", $pascoa), "Corpus Christ");
        $this->setFeriado($this->getSegundoDomingo(5), "Dia das mães (Dia comercial)");
        $this->setFeriado(mktime(0, 0, 0, 1, 1, $this->ano), "Confraternização Universal");
        $this->setFeriado(mktime(0, 0, 0, 4, 21, $this->ano), "Tiradentes");
        $this->setFeriado(mktime(0, 0, 0, 5, 1, $this->ano), "Dia do trabalhador");
   } else if ($mes == 7) {
        $this->setFeriado(mktime(0, 0, 0, 7, 9, $this->ano), "Revolução Constitucionalista de 1932");
   } else if ($mes == 8) {
        $this->setFeriado($this->getSegundoDomingo(8), "Dia dos pais (Dia comercial)");
   } else if ($mes == 9) {
        $this->setFeriado(mktime(0, 0, 0, 9, 7, $this->ano), "Dia da Independência");
   } else if ($mes == 10) {
        $this->setFeriado(mktime(0, 0, 0, 10, 12, $this->ano), "Nossa Senhora Aparecida");
   } else if ($mes == 11) {
        $this->setFeriado(mktime(0, 0, 0, 11, 2, $this->ano), "Todos os Santos");
        $this->setFeriado(mktime(0, 0, 0, 11, 15, $this->ano), "Proclamação da Republica");
   } else if ($mes == 12) {
       $this->setFeriado(mktime(0, 0, 0, 12, 25, $this->ano), "Natal");
   }
}

Agora nossa classe de feriados evoluiu bastante, mas falta um meio para recuperar os dados adicionados ao objeto Feriado. Por, é necessário adicionar o o método Feriado::getFeriados():

/**
 * Retorna um array com os feriados do mês
 * @return array
 */
 public function getFeriados()
 {
     return $this->feriados;
 }

O método Feriado::getFeriados(), retorna o array $feriados, que poderá ser iterado dentro do objeto MeuCalendarioHTML, conforme o mês informado.

Feito isso, nossa classe Feriados está concluída! Veja o código da classe na integra:

/**
 * Classe Feriados
 *
 * @author Alexandre Bezerra Barbosa <alxbbarbosa@yahoo.com.br>
 */
class Feriados
{
    private $ano;
    private $mes;
    private $feriados;

    public function __construct($mes, $ano)
    {
        if (is_int($mes) && $mes <= 12 && $mes >= 1) {
            $this->mes = $mes;
        } else {
            throw new Exception("Erro: o valor definido para mês não é válido: ".$mes);
        }

        if (is_int($ano) && $ano >= 1970 && $ano <= 2099) {
            $this->ano = $ano;
        } else {
            throw new Exception("Erro: o valor definido para ano não é válido: ".$ano);
        }

        if ($mes >= 1 && $mes <= 5) {
            $pascoa = easter_date($this->ano); // Páscoa
            $this->setFeriado($pascoa, "Páscoa");
            $this->setFeriado(strtotime("-48 days", $pascoa), "Carnaval (Ponto-facultativo)");
            $this->setFeriado(strtotime("-47 days", $pascoa), "Carnaval (Ponto-facultativo)");
            $this->setFeriado(strtotime("-46 days", $pascoa), "Quarta-feira de cinzas");
            $this->setFeriado(strtotime("-2 days", $pascoa), "Sexta-feira Santa (Paixão de Cristo)");
            $this->setFeriado(strtotime("+2 days", $pascoa), "Corpus Christ");
            $this->setFeriado($this->getSegundoDomingo(5), "Dia das mães (Dia comercial)");
            $this->setFeriado(mktime(0, 0, 0, 1, 1, $this->ano), "Confraternização Universal");
            $this->setFeriado(mktime(0, 0, 0, 4, 21, $this->ano), "Tiradentes");
            $this->setFeriado(mktime(0, 0, 0, 5, 1, $this->ano), "Dia do trabalhador");
        } else if ($mes == 7) {
            $this->setFeriado(mktime(0, 0, 0, 7, 9, $this->ano), "Revolução Constitucionalista de 1932");
        } else if ($mes == 8) {
            $this->setFeriado($this->getSegundoDomingo(8), "Dia dos pais (Dia comercial)");
        } else if ($mes == 9) {
            $this->setFeriado(mktime(0, 0, 0, 9, 7, $this->ano), "Dia da Independência");
        } else if ($mes == 10) {
            $this->setFeriado(mktime(0, 0, 0, 10, 12, $this->ano), "Nossa Senhora Aparecida");
        } else if ($mes == 11) {
            $this->setFeriado(mktime(0, 0, 0, 11, 2, $this->ano), "Todos os Santos");
            $this->setFeriado(mktime(0, 0, 0, 11, 15, $this->ano), "Proclamação da Republica");
        } else if ($mes == 12) {
            $this->setFeriado(mktime(0, 0, 0, 12, 25, $this->ano), "Natal");
        }
    }

   /**
    * Retorna timestamp referente ao segundo domingo do mês
    * @return int
    */
    private function getSegundoDomingo($mes)
    {
        $timestamp = strtotime("-1 day", mktime(0, 0, 0, $mes, 1, $this->ano));
        $diaNaSemana = date('w', $timestamp);
        $dia = date('d', $timestamp) - $diaNaSemana;

       // Preenche array
        for ($diaSemana = 0; $diaSemana <= $diaNaSemana; $diaSemana++) {
            $arrayDias[$dia++] = $diaSemana;
        }

        for ($dia = 1; $dia < 14; $dia++) {
            $arrayDias[$dia] = date("w", mktime(0, 0, 0, $mes, $dia, $this->ano));
        }

        $c = 0;
        foreach ($arrayDias as $d => $w) {
            if ($w == 0 && $d < 20) $c++;
            if ($c == 2) return mktime(0, 0, 0, $mes, $d, $this->ano);
        }
    }

  /**
    * Configura um feriado para o mês
    * @param int $timestamp
    * @param String $descricao
    */
    private function setFeriado($timestamp, $descricao)
    {
       if (date("n", $timestamp) == $this->mes) {
           $this->feriados[date('j', $timestamp)] = $descricao;
       }
    }

   /**
    * Retorna um array com os feriados do mês
    * @return array
    */
    public function getFeriados()
    {
        return $this->feriados;
    }
}

Mas nosso objeto MeuCalendarioHTML, não conhece a classe feriados e não possui nenhum meio para se comunicar com um objeto Feriado. Precisamos fazer pequenas adições para ser possível esta interação entre os objetos.

Voltando a atenção a classse MeuCalendarioHTML , primeiro adicione um atributo para fazer referência a um objeto Feriados:

private $feriados;

E então adicione também este método MeuCalendarioHTML::setFeriados(Feriado $feriados). O método irá receber uma referência de objeto Feriados e associá-la ao atributo MeuCalendarioHTML::$feriados. Perceba que temos um “type hitting” informando que o objeto precisa ser do tipo Feriado e com isso forçamos uma injeção de dependência:

/**
 * Adicionar Feriados ao calendário
 * @param Feriados $feriados
 */
 public function setFeriados(Feriados $feriados)
 {
     $this->feriados = $feriados->getFeriados();
 }

Por fim, para se trabalhar com os dados do objeto Feriados, será necessário alterar o método MeuCalendarioHTML::getCalendario(), note o que é necessário adicionar:

/**
 * Retorna String html do Calendário
 * @return string Calendário HTML
 */
 public function getCalendario()
 {
     $str = "<table border=\"1\">\n\r";
     $str .= "\t<tr>\n\r\t\t<td colspan=\"7\"><center>\n\r";
     $str .= "\t\t<a href=\"{$this->getLinkMesAnterior()}\"><</a> {$this->getNomeMes()} ";
     $str .= "<a href=\"{$this->getLinkMesSeguinte()}\">></a> ";
     $str .= "de <a href=\"{$this->getLinkAnoAnterior()}\"><</a> {$this->getAno()} ";
     $str .= "<a href=\"{$this->getLinkAnoSeguinte()}\">></a> \n\r\t\t</center></td>\n\r\t<tr>\n\r";
     
     for ($index = 0; $index <= 6; $index++) {
         $str .= "\t\t<th>{$this->getSiglaPorDia($index)}</th>\n\r";
     }

     for ($index = 0; $index <= 41; $index++) {
         // Cor da célula
         $cor = ($index % 7 == 0)? "#D3D3D3" : "#F5F5F5";
         if ($index % 7 == 0) $str .= "\t</tr><tr>\n\r";
         if ($this->isAnterior($index)) {
             $str .= "\t\t<td bgcolor=\"{$cor}\"><i>{$this->getDiasDoMes()[$index]}</i></td>\n\r";
         } else if ($this->isSeguinte($index)) {
             $str .= "\t\t<td bgcolor=\"{$cor}\"><i>{$this->getDiasDoMes()[$index]}</i></td>\n\r";
         } else {
             $dia = $this->getDiasDoMes()[$index];
             $str .= "\t\t<td bgcolor=\"{$cor}\"><a href=\"?d={$dia}&m={$this->getMes()}&a={$this->getAno()}\">{$dia}</a></td>\n\r";
         }
     }

     // Adiciona os feriados
     if (isset($this->feriados) && count($this->feriados) > 0) {
         $str .= "\t<tr>\n\r";
         $str .= "\t\t<td colspan=\"7\">\n\r";
         $str .= "<b>Feriados:</b><br />";
         foreach ($this->feriados as $dia => $feriado) {
             $str .= "{$dia} - {$feriado}<br />";
         }
         $str .= "\t\t</td>\n\r";
         $str .= "\t<tr>\n\r";
     }
     $str .= "\t<tr>\n\r\t</table>\n\r";
     return $str;
 }

Tendo adicionado as linhas necessárias, terminamos tudo que é necessário para se instanciar o calendário com recurso de Feriados. Note como é feito:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <?php
   
     try {
    
         $c = new MeuCalendarioHTML(2, 2016);
         $c->setFeriados(new Feriados(2, 2016));

         echo $c->getCalendario();

     } catch (Exception $e) {
         echo $e->getMessage();
     }
     ?>
     </body>
</html>

Finalmente nosso projeto ficou pronto!

Artigo classe PHP para calendário: Exibindo na tela com Feriados
Artigo classe PHP para calendário: Exibindo na tela com Feriados

Agora você tem um conjunto de classes para gerar rapidamente calendários do mês que precisar. 🙂

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <?php
   
    try {

        echo "<table><tr>";
        for ($m = 1; $m <= 12; $m++) {
            $c = new MeuCalendarioHTML($m, 2016);
            $c->setFeriados(new Feriados($m, 2016));
            echo "<td>".$c->getCalendario()."</td>";
            if ($m % 4 == 0) echo "</tr><tr>";
        }

        echo "</tr></table>";
    } catch (Exception $e) {
        echo $e->getMessage();
    }
    ?>
    </body>
</html>
Artigo classe PHP para calendário: Exibindo o ano inteiro com Feriados
Artigo classe PHP para calendário: Exibindo o ano inteiro com Feriados

Etapa extra – Dando estilo ao nosso calendário

Observando bem, você deve imaginar que os calendários estão todos aparecendo com tamanhos aleatório. Isso é verdade porque utilizamos apenas códigos html sem estilização. Mas a visualização destes pode ser aprimorada e muito com suporte do css. Note a classe MeuCalendarioHTML com pequenas correções e remodelada para associar estilos CSS através do código cliente:

/**
 * Classe MeuCalendarioHTML
 *
 * @author Alexandre Bezerra Barbosa <alxbbarbosa@yahoo.com.br>
 */
 class MeuCalendarioHTML extends MeuCalendario
 {
     private $feriados;
     private $class;
     
     public function __construct($mes, $ano)
     {
         parent::__construct($mes, $ano);
     }

    /**
     * Ajuda a decidir se irá imprimir os dias do mês anterior
     * @param int $indexLoop
     * @return boolean
     */
     private function isAnterior($indexLoop)
     {
         if (is_int($indexLoop)) {
             return (($this->isDiasMesAnterior() == TRUE) && ($this->getDiasDoMes()[$indexLoop] > 25 && $indexLoop < 6)) ? TRUE : FALSE;
         } else {
             throw new Exception("Erro: Valor incorreto indicado como indice de array do calendário: ".$indexLoop);
         }
     }

    /**
     * Ajuda a decidir se irá imprimir os dias do mês seguinte
     * @param int $indexLoop
     * @return boolean
     */
     private function isSeguinte($indexLoop)
     {
         if (is_int($indexLoop)) {
             return (($this->isDiasMesSeguinte() == TRUE) && ($this->getDiasDoMes()[$indexLoop] < 15 && $indexLoop > 27)) ? TRUE : FALSE;
         } else {
             throw new Exception("Erro: Valor incorreto indicado como indice de array do calendário: ".$indexLoop);
         }
     }

    /**
     * Link para mês anterior
     * @return String
     */
     private function getLinkMesAnterior()
     {
         $data = explode("/", date("m/Y", strtotime("-1 month", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Link para mês seguinte
     * @return String
     */
     private function getLinkMesSeguinte()
     {
         $data = explode("/", date("m/Y", strtotime("+1 month", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Link para ano anterior
     * @return String
     */
     private function getLinkAnoAnterior()
     {
         $data = explode("/", date("m/Y", strtotime("-1 year", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Link para ano seguinte
     * @return String
     */
     private function getLinkAnoSeguinte()
     {
         $data = explode("/", date("m/Y", strtotime("+1 year", mktime(0, 0, 0, $this->getMes(), 1, $this->getAno()))));
         return "?m={$data[0]}&a={$data[1]}";
     }

    /**
     * Adicionar Feriados ao calendário
     * @param Feriados $feriados
     */
     public function setFeriados(Feriados $feriados)
     {
         $this->feriados = $feriados->getFeriados();
     }

    /**
     * Retorna String html do Calendário
     * @return string Calendário HTML
     */
     public function getCalendario()
     {
         $str = "<table ";
         if (isset($this->class[0]) && $this->class[0] != null) $str .= "class=\"".$this->class[0]."\"";
         $str .= ">\n\r";
         $str .= "\t<tr>\n\r\t\t<td ";
         if (isset($this->class[1]) && $this->class[1] != null) $str .= "class=\"".$this->class[1]."\"";
             $str .= " colspan=\"7\">\n\r";
             $str .= "\t\t<a href=\"{$this->getLinkMesAnterior()}\"><</a> {$this->getNomeMes()} ";
             $str .= "<a href=\"{$this->getLinkMesSeguinte()}\">></a> ";
             $str .= "de <a href=\"{$this->getLinkAnoAnterior()}\"><</a> {$this->getAno()} ";
             $str .= "<a href=\"{$this->getLinkAnoSeguinte()}\">></a> \n\r\t\t</td>\n\r\t<tr>\n\r";
             for ($index = 0; $index <= 6; $index++) {
                 $str .= "\t\t<th ";
                 if (isset($this->class[2]) && $this->class[2] != null) $str .= "class=\"".$this->class[2]."\"";
                 $str .= ">{$this->getSiglaPorDia($index)}</th>\n\r";
             }

             for ($index = 0; $index <= 41; $index++) {
             // Cor da célula
             if ($index % 7 == 0) $str .= "\t</tr><tr>\n\r";
                 $str .= "\t\t<td ";
                 // Decidir sobre estilização
                 if ($this->isAnterior($index)) {
                     if ($index % 7 == 0 && (isset($this->class[9]) && $this->class[9] != null)) {
                         $class = "class=\"".$this->class[9]."\"";
                     } else if (($index + 1) % 7 == 0 && (isset($this->class[8]) && $this->class[8] != null)) {
                         $class = "class=\"".$this->class[8]."\"";
                     } else if (isset($this->class[10]) && $this->class[10] != null) {
                         $class = "class=\"".$this->class[10]."\"";
                     }
                     $insereLink = FALSE;
                 } else if ($this->isSeguinte($index)) {
                     if ($index % 7 == 0 && (isset($this->class[9]) && $this->class[9] != null)) {
                         $class = "class=\"".$this->class[9]."\"";
                     } else if (($index + 1) % 7 == 0 && (isset($this->class[8]) && $this->class[8] != null)) {
                         $class = "class=\"".$this->class[8]."\"";
                     } else if (isset($this->class[10]) && $this->class[3] != null) {
                         $class = "class=\"".$this->class[10]."\"";
                     }
                     $insereLink = FALSE;
                 } else {
                     $data = $this->getDiasDoMes()[$index]."-".$this->getMes()."-".$this->getAno();
                     if (($data == date("j-n-Y", time())) && isset($this->class[4])) {
                         $class = "class= \"".$this->class[4]."\"";
                     } else if ($index % 7 == 0 && (isset($this->class[6]) && $this->class[6] != null)) {
                         $class = "class=\"".$this->class[6]."\"";
                     } else if (($index + 1) % 7 == 0 && (isset($this->class[7]) && $this->class[7] != null)) {
                         $class = "class=\"".$this->class[7]."\"";
                     } else if (isset($this->class[3]) && $this->class[3] != null) {
                         $class = "class=\"".$this->class[3]."\"";
                     }
                     $insereLink = TRUE;
                 }
                 if (isset($class)) $str .= $class;
                 if (isset($insereLink) && $insereLink == TRUE) {
                     $str .= " ><a href=\"?d={$this->getDiasDoMes()[$index]}&m={$this->getMes()}&a={$this->getAno()}\">{$this->getDiasDoMes()[$index]}</a></td>\n\r";
                 } else {
                     $str .= " >{$this->getDiasDoMes()[$index]}</td>\n\r";
                 }
             }    

             // Adiciona os feriados
             if (isset($this->feriados) && count($this->feriados) > 0) {
                 $str .= "\t<tr>\n\r";
                 $str .= "\t\t<td ";
                 if (isset($this->class[5]) && $this->class[5] != null) $str .= "class=\"".$this->class[5]."\"";
                 $str .= " colspan=\"7\">\n\r";
                 $str .= "<b>Feriados:</b><br />";
                 
                 foreach ($this->feriados as $dia => $feriado) {
                     $str .= "{$dia} - {$feriado}<br />";
                 }
                 $str .= "\t\t</td>\n\r";
                 $str .= "\t<tr>\n\r";
             }
             $str .= "\t<tr>\n\r\t</table>\n\r";
     return $str;
     }

    /**
     * Adiciona estilo ao Calendário
     * @param Int $index
     * @param String $stringClass
     */
     public function setClass($index, $stringClass)
     {
         $this->class[$index] = $stringClass;
     }
}

Veja como instanciar e aplicar o CSS:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Calendário</title>
        <style>

        .classTable {
            font-family: "Times New Roman", Times, serif;
            border-collapse: collapse;
            border: 1px solid black;
            font-size: 12px;
            width: 250px;
            height: 250px;
            text-align: left;
        }

        .classTdHeader {
            border: 1px solid black;
            width: 25px;
            height: 30px;
            font-size: 14px;
            font-weight: bold;
            text-align: center;
            color: white;
            background-color: gray;
        }

        .classTdHeader a:link { text-decoration: none;color : white; }
        .classTdHeader a:visited { text-decoration: none;color : white; }
        .classTdHeader a:active { text-decoration: none;color : white; }
        .classTdHeader a:hover { text-decoration: none;color : red; }
       
        .classTdHeaderDay {
            border: 1px solid black;
            width: 25px;
            height: 30px;
            font-size: 14px;
            font-weight: bold;
            text-align: center;
            color: white;
            background-color: darkgray;
        }

        .classTdCommonDay {
            border: 1px solid black;
            width: 25px;
            height: 30px;
            font-size: 16px;
            font-weight: bold;
            text-align: center;
            background-color: lightgray;
        }

        .classTdCommonDay a:link { text-decoration: none;color : black; }
        .classTdCommonDay a:visited { text-decoration: none;color : black; }
        .classTdCommonDay a:active { text-decoration: none;color : black; }
        .classTdCommonDay a:hover { text-decoration: none;color : red; }
    
        .classTdToday {
            border: 1px solid black;
            width: 25px; height: 30px;
            font-size: 16px;
            font-weight: bold;
            text-align: center;
            background-color: red;
        }

        .classTdToday a:link { text-decoration: none;color : white; }
        .classTdToday a:visited { text-decoration: none;color : white; }
        .classTdToday a:active { text-decoration: none;color : white; }
        .classTdToday a:hover { text-decoration: underline;color : yellow; }

        .classTdHoliday {
            border: 1px solid black;
            height: 60px; font-size: 10px;
            font-weight: bold;
            text-align: left;
            background: white;
        }

        .classTdSunday, .classTdSaturday {
            border: 1px solid black;
            width: 25px; height: 30px;
            font-size: 16px;
            font-weight: bold;
            text-align: center;
            background-color: gray;
        }

        .classTdSunday a:link, .classTdSaturday a:link { text-decoration: none;color : white; }
        .classTdSunday a:visited, .classTdSaturday a:visited { text-decoration: none;color : white; }
        .classTdSunday a:active, .classTdSaturday a:active { text-decoration: none;color : white; }
        .classTdSunday a:hover, .classTdSaturday a:hover { text-decoration: none;color : red; }
       
        .classTdSundayOtherMonth, .classTdSaturdayOtherMonth {
            border: 1px solid black;
            width: 25px; height: 30px;
            font-size: 16px;
            font-weight: bold;
            font-style: italic;
            text-align: center;
            background-color: gray;
            color: lightgrey;
        }

        .classTdOtherMonth {
            border: 1px solid black;
            width: 25px; height: 30px;
            font-size: 16px;
            font-weight: bold;
            font-style: italic;
            text-align: center;
            background-color: lightgray;
            color: gray;
        }
        </style>
    </head>
    <body>
     <?php

     try {

         $c = new MeuCalendarioHTML(9, 2016);
         $c->setFeriados(new Feriados(9, 2016));
         $c->setClass(0, "classTable");
         $c->setClass(1, "classTdHeader");
         $c->setClass(2, "classTdHeaderDay");
         $c->setClass(3, "classTdCommonDay");
         $c->setClass(4, "classTdToday");
         $c->setClass(5, "classTdHoliday");
         $c->setClass(6, "classTdSaturday");
         $c->setClass(7, "classTdSunday");
         $c->setClass(8, "classTdSaturdayOtherMonth");
         $c->setClass(9, "classTdSundayOtherMonth");
         $c->setClass(10, "classTdOtherMonth");
    
         echo $c->getCalendario();

     } catch (Exception $e) {
         echo $e->getMessage();
     }
    ?>
    </body>
</html>

O resultado é muito melhor:

Artigo classe PHP para calendário: Exibindo na tela com estilo
Artigo classe PHP para calendário: Exibindo na tela com estilo

Poderia até associar os nomes em um array e iterar para criar vários de uma só vez!

Feito apenas esta mudança no HTML (OBS: O CSS foi omitdo, mas é o mesmo apresentado acima):

<!DOCTYPE html>
<html>
     <head>
         <meta charset="UTF-8">
         <title>Calendário</title>
         <style>
                  /* / Omitido / */
         </style>
     </head>
     <body>
     <?php

     try {
         $arrayStyle = array("classTable", "classTdHeader", "classTdHeaderDay", "classTdCommonDay", "classTdToday", "classTdHoliday", "classTdSaturday", "classTdSunday", "classTdSaturdayOtherMonth", "classTdSundayOtherMonth", "classTdOtherMonth");
         
         echo "<table><tr>";
         echo "<td colspan=\"6\"><center><h1>Calendário de 2016<h1></center></td>";
         echo "</tr><tr>";
    
         for ($mes = 1; $mes <= 12; $mes++) {
             $c{1} = new MeuCalendarioHTML($mes, 2016);
             $c{1}->setFeriados(new Feriados($mes, 2016));
             foreach ($arrayStyle as $index => $stringClass) {
                 $c{1}->setClass($index, $stringClass);
             }
             echo "<td valign=\"top\">";
             echo $c{1}->getCalendario();
             echo "</td>";

             if ($mes % 6 == 0) echo "</tr><tr>";
         }
         echo "</tr>";
         echo "</table>";

     } catch (Exception $e) {
         echo $e->getMessage();
     }
     ?>
     </body>
</html>
Artigo classe PHP para calendário: Exibindo na tela o ano inteiro com estilo
Artigo classe PHP para calendário: Exibindo na tela o ano inteiro com estilo

Conclusão

Um calendário é sempre um código interessante, algo simples mas até desafiador em alguns momentos. Sim, sempre a algo a a ser melhorado e isto não é diferente com classes acima apresentadas acima. Mas as classes são totalmente funcionais e não perdem para quase nenhum código que esteja sendo oferecido por alguns frameworks atualmente. Note que foram utilizados apenas algumas lógicas diferentes mas com as mesmas funções php. Por fim, isso resultou em uma bela aplicação.

Espero que ter superados suas expectativas!

[]’s

1 comentário

  1. Parabéns Alexandre Barbosa pelo excelente artigo e didática, estou criando uma Classe para Calendários e o conhecimento que adquiri aqui me ajudará e muito para finalizar esta Classe que parece ser simples, mas é algo complexo e bem desafiador.
    Seguindo a lógica de Exibição dos feriados, estou pensando em criar uma Classe para Agenda integrada a Classe Calendário de modo que exiba os Agendamentos do mês logo abaixo de Feriados.

    Ah! Gostei da parte e da explicação sobre os feriados que se baseiam na páscoa.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.