1. Afficher l'IP de l'utilisateur

Toujours utile de l'avoir à porter de main.

<?php
echo $_SERVER['REMOTE_ADDR'];

2. Conserver l'aspect ratio d'un div en CSS


Dans le cas où l'on souhaite qu'un div se comporte comme une image lors du redimensionnement de la page.
Voici le code qui permet de le faire.

En résumé :
- un div container est en position relative ;
- on lui impose un padding top qui gère le ratio ;
- un div à l'intérieur est en position absolue.


📖️️Source : https://www.geeksforgeeks.org/maintain-the-aspect-ratio-of-a-div-with-css/


    <!DOCTYPE html>
    <html>
        <head>
            <meta name="viewport" content="width=device-width,
            initial-scale=1">
            <style>
                .container {
                    background-color: green;
                    position: relative;
                    width: 100%;
                    padding-top: 56.25%; /* 16:9 Aspect Ratio */
                }
    
                .text {
                    position: absolute;
                    top: 0;
                    left: 0;
                    bottom: 0;
                    right: 0;
                    text-align: center;
                    font-size: 25px;
                    color: white;
                }
                .example {
                    background: white;
                    color: green;
                    font-weight:bold;
                    font-size: 40px;
                    padding-bottom: 20px;
                }
            </style>
            </head>
        <body>
            <div class="container">
                <div class="text">
                    <div class = "example">GeeksforGeeks</div>
    
    <p>A Computer Science portal for geeks. It
                    contains well written, well thought and well
                    explained computer science and programming
                    articles, quizzes etc. Also it contains several
                    coding questions to practice and develop your
                    skills in programming.</p>
    
                </div>
            </div>
        </body>
    </html>                               


🧙‍♂️️La source est bien plus utile que cette page mais j'en ai tellement bavé que le jour où j'aurais à nouveau ce besoin, je ne souhaite pas que l'URL de la page source de ce code retourne un 404.

3. DebugDoctrineRepositoryTrait


3.1. Contexte

Doctrine2 utilise une classe Query qui possède deux méthodes, getDQL et getSQL.
Malheureusement, aucune de ces deux méthodes ne permet de savoir quelle est la requête SQL finalement executée par le serveur.

Ce snippet permet d'évaluer ce que serait cette requête.

3.2. Dépendances :

- Doctrine ORM (https://packagist.org/packages/doctrine/orm) pour la classe Query.
- Symfony VarDumper (https://packagist.org/packages/symfony/var-dumper) pour la méthode dump() (pensez à décommenter le code).

3.3. Le fichier DebugDoctrineRepositoryTrait.php

Il faut créer le fichier DebugDoctrineRepositoryTrait.php dans le dossier src/Repository puis l'appeler dans le fichier EntityRepository via un use.

src/Repository/DebugDoctrineRepositoryTrait.php
<?php

namespace App\Repository;

trait DebugDoctrineRepositoryTrait
{

    public static function echoSQL($query)
    {
        echo(self::getRawSQL($query).PHP_EOL);
    }

    /*
    public static function dumpSQL($query)
    {
        dump(self::getRawSQL($query));
    }

    public static function ddSQL($query)
    {
        self::dumpSQL($query);
        die;
    }
    */

    public static function getRawSQL($query)
    {
        $attributesToReplace = [];
        preg_match_all('#:([a-zA-Z_0-9]+)#', $query->getDQL(), $attributesToReplace);
        $rawSQL = $query->getSQL();

        foreach ($attributesToReplace[1] as $attrToReplace) {
            foreach ($query->getParameters()->toArray() as $param) {
                if ($attrToReplace === $param->getName()) {
                    //dump("remplacement de : ". $param->getName() . "par " . $param->getValue());
                    if (is_string($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', '"'.$param->getValue().'"', $rawSQL, 1);
                    } elseif(is_numeric($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', ''.$param->getValue().'', $rawSQL, 1);
                    } elseif(is_bool($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', (int) $param->getValue(), $rawSQL, 1);
                    } elseif(is_array($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', '"'.implode('","', $param->getValue()).'"', $rawSQL, 1);
                    } elseif($param->getValue() instanceof \DateTime) {
                        $rawSQL = preg_replace('#\?#', '"'.$param->getValue()->format('Y-m-d H:i:s').'"', $rawSQL, 1);
                    }
                }
            }
        }

        return $rawSQL;
    }
}


3.4. Exemple d'utilisation

src/Repository/MyEntityRepository.php
<?php
namespace App\Repository;

use Doctrine\ORM\EntityRepository;

/**
 * Class MyEntityRepository
 */
class MyEntityRepository extends EntityRepository
{
    use DebugDoctrineRepositoryTrait;

    /**
     * findOneByColumn
     *
     * @param string $rowId
     *
     * @return MyEntityInterface|null
     *
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findOneByColumn($rowId)
    {
        $query = $this->createQueryBuilder('o')
            ->where('o.rowId = :rowId')
            ->setParameter('rowId', $rowId)
            ->getQuery()
        
        // Affiche la requête SQL.
        self::echoSQL($query);

        return $query->useQueryCache(true)
            ->useResultCache(false)
            ->setMaxResults(1)
            ->getOneOrNullResult();
    }
}

4. Faire un appel HTTP

4.1. Avec l'extension curl

Bien qu'il existe des librairies POO très efficaces (Guzzle / Symfony HTTP Client), il est toujours pratique d'avoir un bout de code PHP Vanilla sous le coude pour faire un simple appel HTTP (lorsque l'on ne souhaite ou ne peut pas utiliser composer).

Voici le code suivant qui utilise curl :

$url = "https://example.com"
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1)
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$output = curl_exec($ch);
curl_close($ch);     


📖️️La liste des options possibles est extrêmement complète : https://www.php.net/manual/fr/function.curl-setopt.php.


4.2. Avec Symfony HTTP Client

Autant aller sur la page officielle : https://symfony.com/doc/current/http_client.html


4.3. Avec Guzzle HTTP

Page officielle : https://docs.guzzlephp.org/en/stable/

L'équipe derrière Guzzle HTTP a comme assez mauvaise réputation d'avoir trop modifié son code pour que chaque version soit compatible avec les autres.

4.4. Avec fopen - fonction de téléchargement

Voici une fonction à utiliser dans un terminal pour télécharger un ou plusieurs fichiers (avec barre de progression et pourcentage).

📖️️Le code n'est pas de moi et provient d'un stackoverflow : https://stackoverflow.com/a/49617294


function downloadFile($url, $filePath)
{
    //echo "Retrieving http header...";
    $header = get_headers("$url");
    $pp = "0";
    //echo json_encode($header, JSON_PRETTY_PRINT);
    $key = key(preg_grep('/\bLength\b/i', $header));
    $type = key(preg_grep('/\bType\b/i', $header));
    $http = substr($header[0], 9, 3);
    $tbytes = @explode(" ",$header[$key])[1];
    $type = @explode("/",explode(" ",$header[$type])[1])[1];
    $targetSize = floor((($tbytes / 1000)/1000))." Mo";
    //echo " Target size: ".floor((($tbytes / 1000)/1000))." Mo || ".floor(($tbytes/1000))." Kb";
    $t = explode("/",$url);
    $remote = fopen($url, 'r');
    $local = fopen($filePath, 'w');
    $read_bytes = 0;
    //echo PHP_EOL;
    while(!feof($remote)) {
        $buffer = fread($remote, intval($tbytes));
        fwrite($local, $buffer);
        $read_bytes += 2048;
        $progress = min(100, 100 * $read_bytes / $tbytes);
        $progress = substr($progress,0 , 6) *4;
        $shell = 10; /* Progress bar width */
        $rt = $shell * $progress / 100;
        echo "\033[35;2m\e[0m Downloading '$url' : [".round($progress,3)."%] ".floor((($read_bytes/1000)*4))."Kb Total:" . $targetSize;
        if ($pp === $shell){$pp=0;};
        if ($rt === $shell){$rt=0;};
        echo str_repeat("█",$rt).str_repeat("=",($pp++)).">@\r";
        usleep(1000);
    }
    //echo " \033[35;2m\e[0mDone [100%]  ".floor((($tbytes / 1000)/1000))." Mo || ".floor(($tbytes/1000))." Kb   \r";
    echo PHP_EOL;
    fclose($remote);
    fclose($local);
}



Si l'on cherche à connaitre l'extension d'un nom de fichier (ex: 'index.html.twig' -> 'twig').
Il existe au moins deux façons d'y arriver.

5.1. Première façon

<?php
echo substr(strrchr($filePath,'.'),1);

5.2. Seconde façon

<?php
echo pathinfo($filePath, PATHINFO_EXTENSION);

5.3. Mauvais code

Le code suivant que l'on peut trouver ailleurs ne fonctionne pas :

<?php
echo str_replace('.','',strstr($filePath, '.'));

Ici, pour $filePath = 'index.html.twig', le résultat serait 'htmltwig', ce qui n'est pas le résultat attendu (on souhaite 'twig').

6. Luhn en base 36

6.1. Proposition d'implémentation

<?php
function convBase($numberInput, $fromBaseInput, $toBaseInput)
{
    if ($fromBaseInput==$toBaseInput) return $numberInput;
    $fromBase = str_split($fromBaseInput,1);
    $toBase = str_split($toBaseInput,1);
    $number = str_split($numberInput,1);
    $fromLen=strlen($fromBaseInput);
    $toLen=strlen($toBaseInput);
    $numberLen=strlen($numberInput);
    $retval='';
    if ($toBaseInput == '0123456789')
    {
        $retval=0;
        for ($i = 1;$i <= $numberLen; $i++)
            $retval = bcadd($retval, bcmul(array_search($number[$i-1], $fromBase),bcpow($fromLen,$numberLen-$i)));
        return $retval;
    }
    if ($fromBaseInput != '0123456789')
        $base10=convBase($numberInput, $fromBaseInput, '0123456789');
    else
        $base10 = $numberInput;
    if ($base10<strlen($toBaseInput))
        return $toBase[$base10];
    while($base10 != '0')
    {
        $retval = $toBase[bcmod($base10,$toLen)].$retval;
        $base10 = bcdiv($base10,$toLen,0);
    }
    return $retval;
}

6.1.1 Exemple d'utilisation

$sring = "AAAAAAAAAAAAAAAAAAAAA";
$a = base_convert($sring, 36, 10);
$gmp = gmp_init($sring, 36);
$b = gmp_strval($gmp, 10);
$c = convBase($sring, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789");
echo "$a";
echo "\n";
echo "$b";
echo "\n";
echo "$c";
echo "\n";

7. Manipuler les XML en PHP


💣️Le code ci-dessous a plusieurs années et il n'est pas forcément encore fonctionnel.


7.1. Itérer à travers les articles d'un flux atom

function getAllFlux($pathFlux)
{
  $document = new DomDocument();
  $document->load($pathFlux);

    $xpath = new DomXPath($document);

    $allFlux = array();
    foreach($xpath->query("//feeds/feed") as $row){
        $keyTmp = $xpath->query("@id",$row)->item(0)->nodeValue;
        $labelTmp = $xpath->query("label",$row)->item(0)->nodeValue;
        $iconTmp = $xpath->query("icon",$row)->item(0)->nodeValue;
        $urlTmp = $xpath->query("url",$row)->item(0)->nodeValue;
        $allFlux[$keyTmp] = array();
        $allFlux[$keyTmp]['label'] = $labelTmp;
        $allFlux[$keyTmp]['url'] = $urlTmp;
        $allFlux[$keyTmp]['iconUrl'] = $iconTmp;
        $allFlux[$keyTmp]['rssUrl'] = $urlTmp;
        $dateHier = gmdate("Y-m-d\TH:i:s\Z", time() - 60 * 60 * 24 * 1);
        $allFlux[$keyTmp]['rssDate'] = $dateHier;
        $allFlux[$keyTmp]['rssUpdateTimeStamp'] = time();
    }
    
    return $allFlux;
}


7.2. Ajouter un noeud dans un flux atom

function addFlux($pathFlux, $label, $url)
{
    $document = new DomDocument();
    $document->load($pathFlux);
    $xpath = new DomXPath($document);
    $parent = $xpath->query("//opml/body");
    $feed = $document->createElement('outline');
    //$feed->appendChild($document->createElement('label', $label));
    //$feed->appendChild($document->createElement('url', $url));
    //$feed->appendChild($document->createElement('icon', $icon));
    $newnode = $parent->item(0)->appendChild($feed);
    $newnode->setAttribute("text", $label);
    $newnode->setAttribute("title", $label);
    $newnode->setAttribute("type", 'rss');
    $newnode->setAttribute("xmlUrl", $url);
    $newnode->setAttribute("htmlUrl", $url);
    $document->saveXML();
    $document->save($pathFlux);
}


7.3. Supprimer tous les noeuds d'un flux atom

function removeAllFlux($pathFlux)
{
  $document = new DomDocument();
  $document->load($pathFlux);
  $xpath = new DomXPath($document);

  $node = $xpath->query('/opml/body/outline');
  foreach ($node as $n) {
    $n->parentNode->removeChild($n);
  }
  $document->saveXML();
  $document->save($pathFlux);
}


7.4. Supprimer un noeud d'un flux atom

function removeFlux($pathFlux, $key)
{
  $document = new DomDocument();
  $document->load($pathFlux);
  $xpath = new DomXPath($document);

  $node = $xpath->query('/opml/body/outline[@title="'.$key.'"]');
  foreach ($node as $n) {
    $n->parentNode->removeChild($n);
  }
  $document->saveXML();
  $document->save($pathFlux);
}


7.5. Appliquer une feuille XSL à un XML

function transformXMLWithXSL($tpl, $xml, $pParams = array() )
{
  $xslDoc = null;
  $xslFnCache = null;
  $xslt = null;

  // Définir les répertoires contenant les feuilles de style
  $cst = get_defined_constants(true);

  // Le document XML
  $xmlDoc = new DomDocument();
  $rc = $xmlDoc->loadXML($xml);

  if ($rc == false){
  throw new Exception('Loading XML principal document via loadXML()',500);
  }

  if(!is_file($tpl) || !is_readable($tpl)){
  throw new Exception('Feuille XSL absente',500);
  }

  // Création du processor
  $xsl = file_get_contents($tpl, true);

  $xslDoc = new DomDocument;
  $rc = $xslDoc->loadXML($xsl);

  if($rc == false){
  throw new Exception('Loading XSL document via loadXML()',500);
  }

  // L'analyseur XSLT
  $xslt=new XSLTProcessor();

  // Autoriser les appels aux fonctions PHP dans une feuille XSL          
  $xslt->registerPHPFunctions();
  $xslt->importStyleSheet($xslDoc);

  $xslt->setParameter('', $pParams);
  $domHtmlText = $xslt->transformToXML($xmlDoc);

  //Correction d'un bug apparent qui importe des xmlns="" dans les premieres balises des templates
  $domHtmlText =str_replace("xmlns=\"\"", "",$domHtmlText);

  return $domHtmlText;
}


7.6. Parser un flux atom

<?php
/**
 * Préfixe du XHTML dans les requêtes XPATH
 */
define('XPATH_PREFIX_XHTML', 'x');

/**
 * Namespace XHTML
*/
define('XPATH_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');

/**
 * Préfixe de Atom dans les requêtes XPATH
*/
define('XPATH_PREFIX_ATOM', 'a');

/**
 * Namespace Atom
*/
define('XPATH_NAMESPACE_ATOM', 'http://www.w3.org/2005/Atom');

/**
 * Préfixe de OpenSearch pour les requêtes XPATH
*/
define('XPATH_PREFIX_OPEN_SEARCH', 'openSearch');

/**
 * Namespace OpenSearch
*/
define('XPATH_NAMESPACE_OPEN_SEARCH', 'http://a9.com/-/spec/opensearchrss/1.0/');

/**
 * Namespace XSL
*/
define('XPATH_NAMESPACE_XSL', 'http://www.w3.org/1999/XSL/Transform');

/**
 * Préfixe de Purl pour les requêtes XPATH
 */
define('XPATH_PREFIX_PURL_CONTENT', 'content');

/**
 * Namespace Purl
*/
define('XPATH_NAMESPACE_PURL_CONTENT', 'http://purl.org/rss/1.0/modules/content/');

/*
 * Get a RSS 
 * 
 * @param $ur
 * @return atom
 *  
 */
function getRss($url)
{
    return file_get_contents($url);
}

/*
 * Conversion de xml à tableau associatif de php
* @param $xml   : XML
* @param string $xpath : xpath de element à récupérer
*
* @return array
*/
function convertXmlToTableau($xml,$xpath)
{
    $list = $xml->xpath($xpath);
    $tableau = array();
    foreach ($list as $elt){
        $classArray = array();
        foreach ($elt as $key => $el){
            $value = (string)$el;
            if(empty($classArray[$key])){
                $classArray[$key] = $value;
            }else{
                $classArray[$key] .= ',' . $value;
            }
        }
        $tableau[] = $classArray ;
    }

    return $tableau;
}

function urlExists($url=NULL)  
{  
    if($url == NULL) return false;  
    $ch = curl_init($url);  
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 50);  
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 50);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    $data = curl_exec($ch);  
    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);  
    curl_close($ch);  

    if($httpcode>=200 && $httpcode<300){  
        return true;  
    } else {  
        return false;  
    }  
}

/**
* Return true if the rss is valid, else false 
*/
function is_valid_rss($url){
    if(urlExists($url)){
        return false;
    }
    $content = getRss($url);
    $xmlContent = getSimpleXMLElement($content);
    if($xmlContent !== false){
        define('XPATH_RSS_ITEM', '/rss/channel/item');
        $rssItems = convertXmlToTableau($xmlContent, XPATH_RSS_ITEM);
        $firstItem = reset($rssItems);
        $link = $firstItem['link'];
        $rssTimestamp = strtotime($firstItem['pubDate']);
        if(filter_var($link, FILTER_VALIDATE_URL)  && $rssTimestamp > 0){

            // Return the title
            define('XPATH_RSS_TITLE', '/rss/channel/title');
            $list = $xmlContent->xpath(XPATH_RSS_TITLE);
            return (string)$list[0];
        }
    }
    return false;   
}

/**
 * Fonction de création d'un objet SimpleXMLElement avec enregistrement des
 * espaces de nom à partir d'une chaine de caractères au format XML.
 *
 * @param string $xmlEntree le flux XML permettant de créer le SimpleXMLElement
 * @param string $namespaceParDefaut le namespace par défaut du flux XML (optionnel)
 * @param string $depuisFichier si vrai, alors $xmlEntree est le <strong>chemin ou d'accès</strong> au contenu à tranformer en SXE.
 * @return SimpleXMLElement L'objet SimpleXMLElement dont le contenu est $xmlEntree ou FALSE en cas d'erreur
 */
function getSimpleXMLElement($xmlEntree, $namespaceParDefaut=false, $depuisFichier=false)
{
    $boolDepuisFichier = chaineEnBooleen($depuisFichier);
    // Création de l'objet SimpleXMLElement
    try {
        if($namespaceParDefaut) {
            // un namespace par défaut a été fourni
            $xmlRetour = @(new SimpleXMLElement($xmlEntree, null, $boolDepuisFichier, $namespaceParDefaut, false));
        } else {
            // pas de namespace par défaut
            $xmlRetour = @(new SimpleXMLElement($xmlEntree, null, $boolDepuisFichier));
        }
    } catch (Exception $e) {
        return false;
    }
    // Enregistrement des espaces de noms
    registerDefaultXPathNamespaces($xmlRetour);
    return $xmlRetour;
}

/**
 * Fonction de transformation d'une chaine en booléen
 *
 * Pour PHP, le cast en booléen de la chaine "false" retourne TRUE,
 * ce qui n'est pas le comportement dont nous avons besoin.
 * Cette fonction retourne un booléen
 * - FALSE si le paramètre casté en booléen retourne faux, ou s'il s'agit de la
 *  chaine "false" ou "faux" (insensible à la casse).
 * - TRUE sinon
 * @param string $chaineTest la chaine à transformer
 * @return bool
 */
function chaineEnBooleen($chaineTest)
{
    if( !(bool)$chaineTest
    || !strncasecmp($chaineTest, 'false', 5)
    || !strncasecmp($chaineTest, 'faux', 4) ) {
        // le paramètre est casté en FALSE ou est une chaine "fausse"
        return false;
    } else {
        return true;
    }
}


/**
 * Enregistre la correspondance entre un prefixe et un namespace pour les requêtes XPATH
 * Agit sur les prefixe a (Atom), x (XHTML) et openSearch
 * @param SimpleXMLElement $xml
 */
function registerDefaultXPathNamespaces(SimpleXMLElement $xml)
{
    $xml->registerXPathNamespace(XPATH_PREFIX_ATOM, XPATH_NAMESPACE_ATOM);
    $xml->registerXPathNamespace(XPATH_PREFIX_XHTML, XPATH_NAMESPACE_XHTML);
    $xml->registerXPathNamespace(XPATH_PREFIX_OPEN_SEARCH, XPATH_NAMESPACE_OPEN_SEARCH);
    $xml->registerXPathNamespace(XPATH_PREFIX_PURL_CONTENT, XPATH_NAMESPACE_PURL_CONTENT);
}


function sanitize_output($buffer)
{
    $search = array(
            '/\>[^\S ]+/s', //strip whitespaces after tags, except space
            '/[^\S ]+\</s', //strip whitespaces before tags, except space
            '/(\s)+/s'  // shorten multiple whitespace sequences
    );
    $replace = array(
            '>',
            '<',
            '\\1'
    );
    $buffer = preg_replace($search, $replace, $buffer);

    return $buffer;
}

8. Player Youtube en JS


📖️️L'article complet ici : https://www.labnol.org/internet/light-youtube-embeds/27941/.

Extrait traduit :
Apprenez à intégrer des vidéos YouTube sur votre site Web de manière rapide et légère, en chargeant la vidéo intégrée à la demande, ce qui permet de réduire la taille de vos pages Web et d'améliorer votre score de base (Web Vitals Google).


8.1. Le code

8.1.1 Partie HTML

Ajouter ceci dans le contenu de la page.

<div class="youtube-player" data-id="VIDEO_ID"></div>


8.1.2 Partie JS

Ajouter ceci dans le contenu de la balise <script> (ou dans un fichier .js)

  <script>
  /*
   * Light YouTube Embeds by @labnol
   * Credit: https://www.labnol.org/
   */

  function loadIframe(div) {
    var iframe = document.createElement('iframe');
    iframe.setAttribute(
      'src',
      'https://www.youtube.com/embed/' + div.dataset.id + '?autoplay=1&rel=0'
    );
    iframe.setAttribute('frameborder', '0');
    iframe.setAttribute('allowfullscreen', '1');
    iframe.setAttribute(
      'allow',
      'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
    );
    div.parentNode.replaceChild(iframe, div);
  }

  function initYouTubeVideos() {
    var playerElements = document.getElementsByClassName('youtube-player');
    for (var n = 0; n < playerElements.length; n++) {
      var videoId = playerElements[n].dataset.id;
      var div = document.createElement('div');
      div.setAttribute('data-id', videoId);
      var thumbNode = document.createElement('img');
      thumbNode.src = '//i.ytimg.com/vi/ID/hqdefault.jpg'.replace(
        'ID',
        videoId
      );
      div.appendChild(thumbNode);
      var playButton = document.createElement('div');
      playButton.setAttribute('class', 'play');
      div.appendChild(playButton);
      div.onclick = function () {
        loadIframe(this);
      };
      playerElements[n].appendChild(div);
    }
  }

  document.addEventListener('DOMContentLoaded', initYouTubeVideos);
</script>


8.1.3 Partie CSS

Enfin, ajouter ceci dans un fichier css.

  .youtube-player {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    max-width: 100%;
    background: #000;
    margin: 5px;
  }

  .youtube-player iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 100;
    background: transparent;
  }

  .youtube-player img {
    object-fit: cover;
    display: block;
    left: 0;
    bottom: 0;
    margin: auto;
    max-width: 100%;
    width: 100%;
    position: absolute;
    right: 0;
    top: 0;
    border: none;
    height: auto;
    cursor: pointer;
    -webkit-transition: 0.4s all;
    -moz-transition: 0.4s all;
    transition: 0.4s all;
  }

  .youtube-player img:hover {
    -webkit-filter: brightness(75%);
  }

  .youtube-player .play {
    height: 72px;
    width: 72px;
    left: 50%;
    top: 50%;
    margin-left: -36px;
    margin-top: -36px;
    position: absolute;
    background: url('//i.imgur.com/TxzC70f.png') no-repeat;
    cursor: pointer;
  }

9. Protéger l'accès à certains fichiers via htpassword (apache2)


9.1. Contexte :

Vous souhaitez interdire l'accès à certaines URLS / Répertoires de votre site web par une simple authentification username/password (authentification basique).

9.2. Protéger un dossier

9.2.1 Se rendre dans la racine du dossier à protéger

cd /var/www/html/secured_dir/

9.2.2 Créer le fichier htpaswword

Taper cette commande pour créer un fichier .htpassword pour l'utilisateur (ici "dmeloni") :

htpasswd -c .htpassword dmeloni


9.2.3 Créer le fichier .htaccess

Créer le fichier .htaccess à la racine de l'application.

💣️Le chemin absolu du fichier de password est TRES important. Sinon cela pointe sur le fichier "/etc/apache2/.htpassword"

AuthName "restricted stuff"
AuthType Basic
AuthUserFile /var/www/html/secured_dir/.htpassword
require valid-user


9.2.4 Ajuster les droits d'accès aux deux fichiers

Mettre les droits suivants sur les fichiers afin que l'utilisateur du service apache2 puisse accéder aux fichiers :

chmod -R 644 .htpassword
chmod -R 644 .htaccess


10. Simuler le mouvement de la souris

Dans le cas où vous avez besoin que le curseur de votre souris soit toujours actif à l'écran (afin d'éviter qu'un programme externe se mette en veille par exemple s'il ne détecte plus la souris), voici un petit script qui fonctionne sous linux.

💣️Ce programme n'est pas optimisé du tout et ne suit pas vraiment les bonnes pratiques.


10.1. Prérequis

Le binaire linux "xdotool" doit être installé.

xdotool -v
# Sortie : 
# xdotool version 3.20160805.1


10.2. Le code à enregistrer dans le fichier simulation.php

Il suffit d'enregistrer le bout de code qui suit et de le lancer directement dans le terminal.

<?php
/**
 * Moves the cursor in case the user does not use it during inactivity time.
 *
 */
$defaultInactivityTime = 240;
$inactivityTime = $defaultInactivityTime; // Time in SECONDES in wich the user is considered as inactive.


function isUserMove()
{
    $mouseLocation = getMouseLocation();
    if ($mouseLocation['x'] > 1) {
        return true;
    }

    return false;
}

/**
 * Move the cursor in the screen.
 */
function simulateMouseMove()
{
    for ($i = 1; $i < 800; $i++) {

        // Stop the process if the user moves the cursor
        if ($i > 5) {
            if (isUserMove()) {
                return false;
            }
        }

        system('xdotool mousemove 1 '.$i);
    }
    for ($i = 800; $i > 1; $i--) {
        system('xdotool mousemove 1 '.$i);

        // Stop the process if the user moves the cursor
        if ($i > 5) {
            $mouseLocation = getMouseLocation();
            if (isUserMove()) {
                return false;
            }
        }
    }

    return true;
}


function getMouseLocation()
{
    $mouseLocation = exec('xdotool getmouselocation 2>&1 | sed -rn \'${s/x:([0-9]+) y:([0-9]+) .*/\1 \2/p}\'');

    $mouseCoordonates = explode(' ', $mouseLocation);

    if (count($mouseCoordonates) === 2) {
       return ['x' => $mouseCoordonates[0], 'y' => $mouseCoordonates[1]];
    }

    return null;
}

function echoState($message, $currentMouseLocation, $color = 'normal')
{
    $redColor = "\033[33m";
    $normalColor = "\033[0m";
//    echo "\ec";
    if ($color == 'red') {
        $currentColor = $redColor;
            $currentModeMessage= '/!\ SECURITY MODE /!\ ';
    } else {
        if (isUserMove()) {
            $currentColor = $normalColor;
            $currentModeMessage= '';
        } else {
            $currentColor = $redColor;
            $currentModeMessage= '/!\ SECURITY MODE /!\ ';
        }
    }


    echo sprintf("%s[%s]{x=%s,y=%s} %s%s".PHP_EOL,
        $currentColor,
        date('Y-m-d H:i:s'),
        str_pad($currentMouseLocation['x'], 4, " " , STR_PAD_LEFT),
        str_pad($currentMouseLocation['y'], 4, " " , STR_PAD_LEFT),
        $currentModeMessage,
        $message
    );
}



$lastState = [
    'time' => time(),
    'mouse_location' => null,
];


$simulationIsActive = false;
while(true) {
    $currentMouseLocation = getMouseLocation();

    if (!is_null($currentMouseLocation)) {
        if (!is_null($lastState['time']) && !is_null($lastState['mouse_location'])) {

            // Detects if the mouse is moved since the last mouvement.
            if (($lastState['mouse_location']['x'] === $currentMouseLocation['x']) &&
                ($lastState['mouse_location']['y'] === $currentMouseLocation['y'])
            ) {
                // Detects if the current user is inactive.
                $diffTimeSinceLastMouvement = time() - $lastState['time'];
                $timeLeftBeforeSimulating = $inactivityTime - $diffTimeSinceLastMouvement;
                if (($lastState['time'] + $inactivityTime) < time()) {
                    echoState('Simulate mouse move !', $currentMouseLocation, 'red');
                    $mouseSimulation = simulateMouseMove();
                    if (false === $mouseSimulation) {
                        // If the user interrupts the simulation.
                        // Reset the inactivity delay.
                        $inactivityTime = $defaultInactivityTime;
                        $simulationIsActive = false;
                    } else {
                        // Is the user is really inactive, reduce the delay.
                        $inactivityTime = rand(5, 30);
                        $simulationIsActive = true;
                    }
                    $lastState['time'] = time();
                } else {
                    echoState(sprintf('No mouse move since %s seconde(s). %s secondes before simulating',
                            $diffTimeSinceLastMouvement,
                            $timeLeftBeforeSimulating
                        ),
                        $currentMouseLocation
                    );
                }
            } else {
                echoState('The mouse has been moved !', $currentMouseLocation);
                $lastState['time'] = time();
            }
        }

        $lastState['mouse_location'] = $currentMouseLocation;
    }

    sleep(1);
}


11. Utiliser un fichier pour la mise en cache


Voici trois fonctions prêtes à l'emploi pour simplement enregistrer des données dans un fichier texte (PHP 5,7,8).

On peut par exemple enregistrer un tableau un peu conséquent en json pour éviter de le recalculer.

Pour rappel, le filemtime retourne le nombre de secondes écoulées depuis la modification du fichier.

function storeCachedContent($filePath, $content)
{
    return file_put_contents($filePath, $content);
}

function isCached($cacheFile, $expireTime = 360)
{
    if(file_exists($cacheFile) && filemtime($cacheFile) > $expireTime){
        return true;
    }

    return false;
}

function getCachedContent($cacheFile)
{
    return @file_get_contents($cacheFile);
}


11.1. Compresser les données


function store($file,$datas)
{
    return file_put_contents($file,gzdeflate(json_encode($datas)));
}

function unstore($file)
{
    return json_decode(gzinflate(file_get_contents($file)),true);
}


12. Distance en mètres entre deux points avec coordonnées GPS

12.1. Le code

<?php
/*---------------------------------------------------------------*/
/*
    Titre : Distance en mètre entre deux points avec coordonnées GPS                                                    
                                                                                                                          
    URL   : https://phpsources.net/code_s.php?id=459
    Auteur           : forty                                                                                              
    Website auteur   : http://www.toplien.fr/                                                                             
    Date édition     : 25 Sept 2008                                                                                       
    Date mise à jour : 10 Aout 2019                                                                                      
    Rapport de la maj:                                                                                                    
    - fonctionnement du code vérifié                                                                                    
    - modification de la description                                                                                      
*/
/*---------------------------------------------------------------*/
    // renvoi la distance en mètres
    function get_distance_m($lat1, $lng1, $lat2, $lng2) {
      $earth_radius = 6378137;   // Terre = sphère de 6378km de rayon
      $rlo1 = deg2rad($lng1);
      $rla1 = deg2rad($lat1);
      $rlo2 = deg2rad($lng2);
      $rla2 = deg2rad($lat2);
      $dlo = ($rlo2 - $rlo1) / 2;
      $dla = ($rla2 - $rla1) / 2;
      $a = (sin($dla) * sin($dla)) + cos($rla1) * cos($rla2) * (sin($dlo) * sin($dlo));
      $d = 2 * atan2(sqrt($a), sqrt(1 - $a));

      return ($earth_radius * $d);
    }


12.2. Exemple d'utilisation

<?php
    echo (round(get_distance_m(48.856667, 2.350987, 45.767299, 4.834329) / 1000, 3)). ' km'; // affiche 391.613 km