Search code examples
phpclassnamespacesextendsmatomo

Extend matomo/piwik device detector class to add new client parser (namespace issue)


I am trying to add a custom user agent for parsing a custom mobile application user agent along with the existing parsers. I tried following the issue over here: https://github.com/matomo-org/device-detector/issues/5931 but could not do it properly.

Directory Structure

UAParserService
|
|_ composer.json
|_ Vendor
|_ index.php
|_ custom_apps.yml
|_ CustomAppParser.php
|_ DeviceDetector.php 

index.php

<?php

require_once "vendor/autoload.php";

use DeviceDetector\DeviceDetector;
use DeviceDetector\Parser\Client\CustomAppParser;

$userAgent = "MyApp/1.0.0 (Linux; Android 9; ONEPLUS A6010)"; // Android App

$dd = new DeviceDetector($userAgent);
$parser = new CustomAppParser();
$dd -> addClientParser($parser);
$dd -> parse();

// Check if user agent is a bot
$isBot = $dd -> isBot();

if($isBot) {
    echo json_encode(["is_bot" => $isBot]);
}
else {
    $clientInfo = $dd->getClient();
    $osInfo = $dd->getOs();
    $device = $dd->getDeviceName();
    $brand = $dd->getBrandName();
    $model = $dd->getModel();

    echo json_encode([
        "is_bot" => $isBot,
        "client_info" => $clientInfo,
        "os_info" => $osInfo,
        "device_type" => $device,
        "device_brand" => $brand,
        "device_model" => $model,
    ], JSON_PRETTY_PRINT);
}

DeviceDetector.php

<?php


    namespace UAParserService\DeviceDetector;
    
    use function array_pop;
    use function array_unshift;
    
    class DeviceDetector extends \DeviceDetector\DeviceDetector
    {
        public function addClientParser($parser){
            parent::addClientParser($parser);
    
            $item = array_pop($this -> clientParsers);
            array_unshift($this -> clientParsers, $item);
        }
    }

CustomAppParser.php

<?php


namespace DeviceDetector\Parser\Client;


class CustomAppParser extends ClientParserAbstract
{
    protected $fixtureFile = "custom_apps.yml";
    protected $parserName  = "mobile app";

    protected function getRegexesDirectory()
    {
        return dirname(__DIR__);
    }
}  

composer.json

{
  "require": {
    "piwik/device-detector": "3.11.7",
    "ext-json": "*"
  }
}

I am extremely unfriendly with namespaces so I might be doing it totally wrong.

I also do get some errors:

( ! ) Fatal error: Uncaught Error: Class 'DeviceDetector\Parser\Client\FurryAppParser' not found in F:\web projects\project1\UAParserService\index.php on line 17

( ! ) Error: Class 'DeviceDetector\Parser\Client\CustomAppParser' not found in F:\web projects\project1\UAParserService\index.php on line 17


Solution

  • First of all, you should consider to put your own classes into dedicated folder, like src.

    After that you can add

      "autoload": {
        "psr-4": {
          "UAParserService\\": "src/"
        }
      }
    

    to your composer.json and update autoloader with composer dump-autoload command.

    At this point you will have setup for use of your namespace.

    Things to note:

    • all your classes in src folder must start its namespace with UAParserService\
    • Filename must match classname (case sensitive)
    • Folder structure must match namespace's elements (case sensitive)
    • UAParserService element will be stripped while searching folders for your class

    For example, if DeviceDetector class have namespace UAParserService\DeviceDetector; it must be placed into scr/DeviceDetector/DeviceDetector.php file. The same goes for CustomAppParser class.

    Also, if you want to use your own DeviceDetector in index.php you should update use statement to
    use UAParserService\DeviceDetector\DeviceDetector;

    Side note: please don't use spaces around -> operator for methods calls ;). At least either use it everywhere, either don't use it at all...

    P.S.: please consider to rename your question to reflect real issue it solves ;). I mean we are solving here namespace issue, not extending issue.