src/Controller/View/Magazine/TocController.php line 228

Open in your IDE?
  1. <?php
  2. namespace App\Controller\View\Magazine;
  3. use App\Application\Sonata\MediaBundle\Entity\Media;
  4. use App\Entity\Account\Subscription;
  5. use App\Entity\Magazine\Magazine;
  6. use App\Entity\Magazine\MagazinePage;
  7. use App\Entity\Magazine\TOC;
  8. use App\Entity\Project\Publisher;
  9. use App\Entity\Api\Gatrack;
  10. use App\Entity\Project\PublisherStore;
  11. use App\Service\AdminEngineService;
  12. use App\Service\SerializePageService;
  13. use Exception;
  14. use function PHPSTORM_META\type;
  15. use Symfony\Component\HttpFoundation\File\File;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\HttpFoundation\Response;
  18. use Symfony\Component\Routing\Annotation\Route;
  19. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  20. use Symfony\Component\Serializer\Encoder\JsonEncoder;
  21. use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
  22. use Symfony\Component\Serializer\Serializer;
  23. use Sabberworm\CSS\Parser;
  24. use Symfony\Component\HttpFoundation\RequestStack;
  25. use Doctrine\ORM\EntityManagerInterface;
  26. use Symfony\Component\HttpFoundation\Cookie;
  27. /**
  28.  * @Route("/view/toc")
  29.  */
  30. class TocController extends AbstractController
  31. {
  32.     private $requestStack;
  33.     private $adminEngine;
  34.     private $em;
  35.     public function __construct(RequestStack $requestStackEntityManagerInterface $emAdminEngineService $adminEngine)
  36.     {
  37.        $this->requestStack $requestStack;
  38.        $this->adminEngine $adminEngine;
  39.        $this->em $em;
  40.     }
  41.     /**
  42.      * @Route("/{slug}", methods={"GET"})
  43.      */
  44.     public function show(Request $request$slug)
  45.     {
  46.         $magazine $this->getDoctrine()->getRepository(Magazine::class)->findOneBy(['slug'=>$slug'paid'=>false]);
  47.         if(!$magazine) {
  48.             return $this->json(['message' => 'magazine not found'], Response::HTTP_NOT_FOUND);
  49.         }
  50.         $store $this->getDoctrine()->getRepository(PublisherStore::class)->findStoreByMag($slug);
  51.         if(!$store) {
  52.             return $this->json(['message' => 'store not found'], Response::HTTP_NOT_FOUND);
  53.         }
  54.         $publisherStore $this->getDoctrine()->getRepository(PublisherStore::class)->findActiveMagInStore($store['slug']);
  55.         if(!$publisherStore) {
  56.             return $this->json(['message' => 'Publisher store cant find'], Response::HTTP_NOT_FOUND);
  57.         }
  58.         $store $this->adminEngine->getRelatedMagazines($publisherStore);
  59.         if($request->get('provider') != 'preprod' && $magazine->getPreprod() == true) {
  60.             return $this->json(['message' => 'Magazine not found'], Response::HTTP_NOT_FOUND);
  61.         }
  62.         $pages $this->getDoctrine()->getRepository(MagazinePage::class)->findBy(['magazine'=>$magazine->getId()], ['position' => 'ASC']);
  63.         if(!$pages) {
  64.             return $this->json(['message' => 'pages not found'], Response::HTTP_NOT_FOUND);
  65.         }
  66.         $addcookieHeader false;
  67.         $deviceId =  "";
  68.         if($slug == "incorsica-70"){
  69.             $userid $request->cookies->get('ga_inc70'); // $user is a string (the userID) or null if there is no cookie.
  70.             if (empty($userid)) {
  71.                 $deviceId =  $this->generateIDUtil();
  72.                 $addcookieHeader true;
  73.             }
  74.             else{
  75.                 $deviceId $userid;
  76.             }
  77.             $gatrack $this->getDoctrine()->getRepository(Gatrack::class)->findOneBy(['deviceId'=>$deviceId]);
  78.             $ip $this->requestStack->getMasterRequest()->getClientIp();
  79.             if($ip == 'unknown' || $ip == "127.0.0.1"){
  80.                 $ip $_SERVER['REMOTE_ADDR'];
  81.             }
  82.             $device $request->headers->get('User-Agent');
  83.             if($device){
  84.                 $device trim(strtolower($device));
  85.             }
  86.             $location "";
  87.             $origin $request->headers->get('origin');
  88.             $country $this->ip_info($ip"Country");
  89.             if($country){
  90.                 $location $country;
  91.             }
  92.             if(!$gatrack){
  93.                 $gatrack = new Gatrack();
  94.                 $gatrack->setIp($ip);
  95.                 $gatrack->setDevice($device);
  96.                 $gatrack->setLocation($location);
  97.                 $gatrack->setCount(1);
  98.                 $gatrack->setDeviceId($deviceId);
  99.                 $gatrack->setOrigin($origin);
  100.             }
  101.             else{
  102.                 $count $gatrack->getCount();
  103.                 $gatrack->setDeviceId($deviceId);
  104.                 $gatrack->setCount($count 1);
  105.             }
  106.              try {
  107.                 $this->em->persist($gatrack);
  108.                 $this->em->flush();
  109.             } catch (\Exception $e) {
  110.                // return new JsonResponse(["error" => $e->getMessage()], 500);
  111.             }
  112.         }
  113.         $count $this->getDoctrine()->getRepository(MagazinePage::class)->countPagesBy($slug);
  114.         $magazine->setTotalPages((int) $count);
  115.         //$magazineEditedCss = preg_replace('~[\r\n]+~', "", $magazine->getCss());
  116.         /*$oParser = new Parser($magazineEditedCss);
  117.         $oCss = $oParser->parse();*/
  118.         //$magazine->addCssArr($magazineEditedCss);
  119.         /*foreach($oCss->getAllDeclarationBlocks() as $oBlock) {
  120.             dump($oBlock);die;
  121.             dump('-----');die;
  122.         }
  123.         dump('donzo');die;*/
  124.         // $magazine->setCss($magazineEditedCss);
  125.         $encoder = new JsonEncoder();
  126.         $normalizer = new ObjectNormalizer();
  127.         $normalizer->setIgnoredAttributes([
  128.             'enabled''file''subscriptions''users''root''parent''lft''lvl''rgt''pages''name''javascript''stylesheet''slug''version''startDate',
  129.             'endDate''cover''publisher''__initializer__''__cloner__''__isInitialized__''content''template''magazine''gallery''routeName''pageAlias''createdAt''updatedAt''collection''cssarr''css''products'
  130.         ]);
  131.         $normalizer2 = new ObjectNormalizer();
  132.         $normalizer2->setIgnoredAttributes([
  133.             'enabled''file''subscriptions''users''root''parent''lft''lvl''rgt''pages''javascript''stylesheet''slug''version''startDate',
  134.             'endDate''cover''publisher''__initializer__''__cloner__''__isInitialized__''content''template''magazine''galleryHasMedias''routeName''pageAlias''createdAt''updatedAt''collection''cssarr''css''products'
  135.         ]);
  136.         $normalizer3 = new ObjectNormalizer();
  137.         $normalizer3->setIgnoredAttributes([
  138.             'enabled''file''subscriptions''users''root''parent''lft''lvl''rgt''toc''createdAt''updatedAt''startedAt''user''__initializer__''__cloner__''__isInitialized__''pages',
  139.             'magazine''collection''cssarr''css''products'
  140.         ]);
  141.         $normalizer4 = new ObjectNormalizer();
  142.         $normalizer4->setIgnoredAttributes([
  143.             'enabled''file''subscriptions''users''root''parent''lft''lvl''rgt''javascript''stylesheet''slug''version''startDate',
  144.             'endDate''cover''publisher''__initializer__''__cloner__''__isInitialized__''content''template''magazine''gallery''routeName''pageAlias''createdAt''updatedAt''collection''cssarr''css''products'
  145.         ]);
  146.         $normalizer->setCallbacks([
  147.             'thumbnail' => $this->callbackImage(),
  148.             'cover' => $this->callbackImage(),
  149.             'download' => $this->callbackDownload(),
  150.             'publisher' => $this->callbackPublisher(),
  151.             'startDate' => $this->callbackDatetime(),
  152.             'endDate' => $this->callbackDatetime(),
  153.             // 'toc' => $this->callbackToc(),
  154.         ]);
  155.         $normalizer2->setCallbacks([
  156.             'thumbnail' => $this->callbackImage(),
  157.             'cover' => $this->callbackImage(),
  158.             'download' => $this->callbackDownload(),
  159.             'publisher' => $this->callbackPublisher(),
  160.             'startDate' => $this->callbackDatetime(),
  161.             'endDate' => $this->callbackDatetime(),
  162.             'gallery' => $this->callbackHasGallery(),
  163.         ]);
  164.         $normalizer3->setCallbacks([
  165.             'cover' => $this->callbackImage(),
  166.             'thumbnail' => $this->callbackImage(),
  167.             'publisher' => $this->callbackPublisher(),
  168.             'logo' => $this->callbackImage(),
  169.             'download' => $this->callbackDownload(),
  170.             'startDate' => $this->callbackDatetime(),
  171.             'endDate' => $this->callbackDatetime(),
  172.             'imageJson' => $this->callbackJson(),
  173.             'bgJson' => $this->callbackJson(),
  174.             'productJson' => $this->callbackJson(),
  175.             'fontJson' => $this->callbackJson(),
  176.             'themeBgColor' => $this->callbackColor(),
  177.             'themeTextColor' => $this->callbackColor(),
  178.             'contrastBgColor' => $this->callbackColor(),
  179.             'contrastTextColor' => $this->callbackColor(),
  180.             'mediaIconColor' => $this->callbackColor(),
  181.             'opacityColor' => $this->callbackColor(),
  182.             'translations' => $this->callbackTranslation(),
  183.         ]);
  184.         $normalizer4->setCallbacks([
  185.             'thumbnail' => $this->callbackImage(),
  186.             'cover' => $this->callbackImage(),
  187.             'download' => $this->callbackDownload(),
  188.             'publisher' => $this->callbackPublisher(),
  189.             'startDate' => $this->callbackDatetime(),
  190.             'endDate' => $this->callbackDatetime(),
  191.         ]);
  192.         $normalizer->setCircularReferenceHandler(function ($object) {
  193.             return $object->getId();
  194.         });
  195.         $normalizer2->setCircularReferenceHandler(function ($object) {
  196.             return $object->getId();
  197.         });
  198.         $normalizer3->setCircularReferenceHandler(function ($object) {
  199.             return $object->getId();
  200.         });
  201.         $toc $magazine->getToc();
  202.         if(!$toc) {
  203.             return $this->json(['message' => 'toc not found'], Response::HTTP_NOT_FOUND);
  204.         }
  205.         $toc $this->getDoctrine()->getRepository(TOC::class)->findBy(['id' => $toc->getId()], ['position' => 'ASC']);
  206.         $serializer = new Serializer(array($normalizer), array($encoder));
  207.         $serializer2 = new Serializer(array($normalizer2), array($encoder));
  208.         $serializer3 = new Serializer(array($normalizer3), array($encoder));
  209.         $serializer4 = new Serializer(array($normalizer4), array($encoder));
  210.         //dump($toc);
  211.         $jtoc $serializer->normalize($magazine->getToc(), 'json');
  212.         // TODO :: Clean + Fix recursive global change below
  213.         usort($jtoc['children'], array($this'sortByPos'));
  214.         $chapteredArr = [];
  215.         foreach ($jtoc['children'] as &$jchild) {
  216.             /*if (count($jchild['children']) > 1) {*/
  217.                 //usort($jchild['children'], array($this, 'sortByPos'));
  218.                 $chapteredArrchild['title'] = $jchild['title'];
  219.                 $chapteredArrchild['start'] = $jchild['start'];
  220.                 $chapteredArrchild['end'] = $jchild['end'];
  221.                 $chapteredArrchild['thumbnail'] = $jchild['thumbnail'];
  222.                 $chapteredArrchild['pages'] = [];
  223.                 array_push($chapteredArr$chapteredArrchild);
  224.                 foreach ($jchild['children'] as &$jjchild) {
  225.                     if (count($jjchild['children']) > 1) {
  226.                         usort($jjchild['children'], array($this'sortByPos'));
  227.                     }
  228.                 }
  229.             /*}*/
  230.         }
  231.         function insertArrayAtPosition$array$insert$position$serializer2 ) {
  232.             /*
  233.             $array : The initial array i want to modify
  234.             $insert : the new array i want to add, eg array('key' => 'value') or array('value')
  235.             $position : the position where the new array will be inserted into. Please mind that arrays start at 0
  236.             */
  237.             /*dump($serializer2->normalize(array_slice($array, 0, $position, TRUE), 'json'));
  238.             dump($serializer2->normalize($insert, 'json'));
  239.             dump($serializer2->normalize(array_slice($array, $position, NULL, TRUE), 'json'));
  240.             dump('-----------');
  241.             dump($serializer2->normalize(array_slice($array, 0, $position, TRUE) + $insert + array_slice($array, $position, NULL, TRUE), 'json'));
  242.             dump('===========');*/
  243.             return array_slice($array0$positionTRUE) + $insert array_slice($array$positioncount($array) - 1TRUE);
  244.         }
  245.         function insertValueAtPosition($arr$insertedArray$position$serializer2) {
  246.             /*dump($position);
  247.             dump($serializer2->normalize($arr, 'json'));*/
  248.             $i 0;
  249.             $new_array=[];
  250.             foreach ($arr as $key => $value) {
  251.                 if ($i == $position) {
  252.                     foreach ($insertedArray as $ikey => $ivalue) {
  253.                         $new_array[$ikey] = $ivalue;
  254.                     }
  255.                 }
  256.                 $new_array[$key] = $value;
  257.                 $i++;
  258.             }
  259.             return $new_array;
  260.         }
  261.         /**
  262.          * Reposition an array element by its key.
  263.          *
  264.          * @param array      $array The array being reordered.
  265.          * @param string|int $key They key of the element you want to reposition.
  266.          * @param int        $order The position in the array you want to move the element to. (0 is first)
  267.          *
  268.          * @throws \Exception
  269.          */
  270.         function repositionArrayElement(array &$array$keyint $order$insert)
  271.         {
  272.             array_push($array$insert);
  273.             if(($a array_search($keyarray_keys($array))) === false){
  274.                 throw new \Exception("The {$key} cannot be found in the given array.");
  275.             }
  276.             $p1 array_splice($array$a1);
  277.             $p2 array_splice($array0$order);
  278.             $array array_merge($p2$p1$array);
  279.         }
  280.         //$magazinePageEditedCss = "";
  281.         $j 0;
  282.         $prevChaptered 0;
  283.         $singleChapterCount 0;
  284.         $isAdvert true;
  285.         $advertChapterExist false;
  286.         foreach ($pages as $i => $page) {
  287.             //if ($i = 7) die;
  288.             $isChaptered false;
  289.             /*if($page->getStylesheet() !== null && $page->getStylesheet() !== "") {
  290. //                $magazineEditedCss .= "//----- page-". $page->getPosition() ."-----/";
  291. //                $magazineEditedCss = $magazineEditedCss . preg_replace('~[\r\n]+~', "", $page->getStylesheet());
  292.                 if (strlen($magazinePageEditedCss) >= 9000) {
  293.                     $magazine->addCssArr($magazinePageEditedCss);
  294.                     $magazinePageEditedCss = "";
  295.                 }
  296.                 $magazinePageEditedCss .= "//---- page-". $page->getPosition() ."----/";
  297.                 $magazinePageEditedCss = $magazinePageEditedCss . preg_replace('~[\r\n]+~', "", $page->getStylesheet());
  298.             }*/
  299.             if ((int)$page->getPosition() !== 1) {                // skip first/cover page
  300.                 foreach ($chapteredArr as $j => $chapter) {
  301.                     if (!is_array($chapter))
  302.                         continue;
  303.                     if ($chapter instanceof Chapter)
  304.                         continue;
  305.                     if (count($chapteredArr) - != (int)$j) {
  306.                         if (is_array($chapteredArr[$j+1]))
  307.                             $end = ($chapter['end']) ?: (((int)$chapteredArr[$j 1]['start'] - < (int)$chapter['start']) ? (int)$chapter['start'] : (int)$chapteredArr[$j 1]['start'] - 1);
  308.                         else
  309.                             $end = ($chapter['end']) ?: (((int)$chapteredArr[$j+1]->start < (int)$chapter['start']) ? (int)$chapter['start'] : (int)$chapteredArr[$j+1]->start 1);
  310.                     } else
  311.                         $end = ($chapter['end']) ?: count($pages);
  312.                     if (is_numeric($page->getPageno()) && ((int)$page->getPageno() >= (int)$chapter['start']) && ((int)$page->getPageno() <= (int)$end)) {
  313.                         array_push($chapteredArr[$j]['pages'], $page);
  314.                         $isChaptered true;
  315.                         $prevChaptered $j;
  316.                         $isAdvert false;
  317.                         break;
  318.                     } elseif ($isAdvert) {
  319.                         // create Advert chapter
  320.                         if (!$advertChapterExist) {
  321.                             $advertChapter = new Chapter();
  322.                             $advertChapter->title "Advert";
  323.                             $advertChapterExist true;
  324.                             repositionArrayElement($chapteredArrcount($chapteredArr), 1$advertChapter);
  325.                         }
  326.                         array_push($advertChapter->pages$page);
  327.                         $isChaptered true;
  328.                         break;
  329.                     }
  330.                 }
  331.             }
  332.             if (!$isChaptered) {
  333.                 $myChapter = new Chapter();
  334.                 $myChapter->title = ($page->getPosition() === 1)? "Cover" $page->getName();
  335.                 $myChapter->start $page->getPageno();
  336.                 $myChapter->end $page->getPageno();
  337.                 $myChapter->thumbnail $page->getThumbnail();
  338.                 array_push($myChapter->pages$page);
  339.                 repositionArrayElement($chapteredArrcount($chapteredArr), $prevChaptered $singleChapterCount$myChapter);
  340.                 $singleChapterCount++;
  341.             }
  342.         }
  343.         //$magazine->addCssArr($magazinePageEditedCss);
  344.         // TODO :: Fix ^
  345.         // $this->orderByPos($jtoc);
  346.         $response =  $this->json(['detail' => $serializer3->normalize($magazine'json'), 'articles' => $jtoc'pages' => $serializer2->normalize($pages'json'), 'chapter_pages' => $serializer4->normalize($chapteredArr'json'), 'affiliated' => $serializer4->normalize($store'json')]);
  347.         if($addcookieHeader){
  348.             $cookie = new Cookie('ga_inc70'$deviceId, (time() + (365 24 60 60)), '/'nulltruefalsefalse'None' );  // Expires 1 years
  349.             $response->headers->setCookie($cookie);
  350.         }
  351.         return $response;
  352.     }
  353.     private function ip_info($ip NULL$purpose "location"$deep_detect TRUE) {
  354.         $output NULL;
  355.         if (filter_var($ipFILTER_VALIDATE_IP) === FALSE) {
  356.             $ip $_SERVER["REMOTE_ADDR"];
  357.             if ($deep_detect) {
  358.                 if (filter_var(@$_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP))
  359.                     $ip $_SERVER['HTTP_X_FORWARDED_FOR'];
  360.                 if (filter_var(@$_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP))
  361.                     $ip $_SERVER['HTTP_CLIENT_IP'];
  362.             }
  363.         }
  364.         $purpose    str_replace(array("name""\n""\t"" ""-""_"), NULLstrtolower(trim($purpose)));
  365.         $support    = array("country""countrycode""state""region""city""location""address");
  366.         $continents = array(
  367.             "AF" => "Africa",
  368.             "AN" => "Antarctica",
  369.             "AS" => "Asia",
  370.             "EU" => "Europe",
  371.             "OC" => "Australia (Oceania)",
  372.             "NA" => "North America",
  373.             "SA" => "South America"
  374.         );
  375.         if (filter_var($ipFILTER_VALIDATE_IP) && in_array($purpose$support)) {
  376.             $ipdat = @json_decode(file_get_contents("http://www.geoplugin.net/json.gp?ip=" $ip));
  377.             if (@strlen(trim($ipdat->geoplugin_countryCode)) == 2) {
  378.                 switch ($purpose) {
  379.                     case "location":
  380.                         $output = array(
  381.                             "city"           => @$ipdat->geoplugin_city,
  382.                             "state"          => @$ipdat->geoplugin_regionName,
  383.                             "country"        => @$ipdat->geoplugin_countryName,
  384.                             "country_code"   => @$ipdat->geoplugin_countryCode,
  385.                             "continent"      => @$continents[strtoupper($ipdat->geoplugin_continentCode)],
  386.                             "continent_code" => @$ipdat->geoplugin_continentCode
  387.                         );
  388.                         break;
  389.                     case "address":
  390.                         $address = array($ipdat->geoplugin_countryName);
  391.                         if (@strlen($ipdat->geoplugin_regionName) >= 1)
  392.                             $address[] = $ipdat->geoplugin_regionName;
  393.                         if (@strlen($ipdat->geoplugin_city) >= 1)
  394.                             $address[] = $ipdat->geoplugin_city;
  395.                         $output implode(", "array_reverse($address));
  396.                         break;
  397.                     case "city":
  398.                         $output = @$ipdat->geoplugin_city;
  399.                         break;
  400.                     case "state":
  401.                         $output = @$ipdat->geoplugin_regionName;
  402.                         break;
  403.                     case "region":
  404.                         $output = @$ipdat->geoplugin_regionName;
  405.                         break;
  406.                     case "country":
  407.                         $output = @$ipdat->geoplugin_countryName;
  408.                         break;
  409.                     case "countrycode":
  410.                         $output = @$ipdat->geoplugin_countryCode;
  411.                         break;
  412.                 }
  413.             }
  414.         }
  415.         return $output;
  416.     }
  417.     private function generateIDUtil() {
  418.         return bin2hex(random_bytes(4)) . substr(base_convert(md5(time()), 16,32), 06);;
  419.     }
  420.     private function callbackImage()
  421.     {
  422.         $callback = function ($image) {
  423.             return $image instanceof Media
  424.                 $image->getUrlMethod()
  425.                 : null;
  426.         };
  427.         return $callback;
  428.     }
  429.     private function orderByPos($tg) {
  430.         foreach ($tg['children'] as &$jchild) {
  431.             if (count($jchild['children']) > 1) {
  432.                 usort($jchild['children'], array($this'sortByPos'));
  433.                 $this->orderByPos($jchild);
  434.             }
  435.         }
  436.     }
  437.     private function sortByPos($a$b) {
  438.         // dump($a);die;
  439.         return $a['position'] > $b['position'];
  440.     }
  441.     private function callbackDownload()
  442.     {
  443.         $callback = function ($pdf) {
  444.             return $pdf instanceof Media
  445.                 $pdf->getUrlMethod()
  446.                 : null;
  447.         };
  448.         return $callback;
  449.     }
  450.     private function callbackTranslation()
  451.     {
  452.         $callback = function ($translations) {
  453.             if (!$translations->isEmpty()) {
  454.                 foreach ($translations as $translation)
  455.                     $arrtranslations[] = [
  456.                         $translation->getLocale() => $translation->getSlug(),
  457.                     ];
  458.                 return $arrtranslations;
  459.             }else
  460.                 return null;
  461.         };
  462.         return $callback;
  463.     }
  464.     private function callbackJson()
  465.     {
  466.         $callback = function ($imageJson) {
  467.             if ($imageJson != null)
  468.                 return json_decode($imageJsontrue);
  469.             else
  470.                 return null;
  471.         };
  472.         return $callback;
  473.     }
  474.     private function callbackDatetime()
  475.     {
  476.         $callback = function ($dateTime) {
  477.             return $dateTime instanceof \DateTime
  478.                 $dateTime->format(\DateTime::ISO8601)
  479.                 : $dateTime;
  480.         };
  481.         return $callback;
  482.     }
  483.     private function callbackHasGallery()
  484.     {
  485.         $callback = function ($gallery) {
  486.             if ($gallery != null)
  487.                 return true;
  488.             else
  489.                 return null;
  490.         };
  491.         return $callback;
  492.     }
  493.     private function callbackColor()
  494.     {
  495.         $callback = function ($color) {
  496.             if ($color != null) {
  497.                 (strlen($color) === 4) ? list($r$g$b) = sscanf($color"#%1x%1x%1x") : list($r$g$b) = sscanf($color"#%2x%2x%2x");
  498.                 $color $r "," $g "," $b;
  499.             } else {
  500.                 $color null;
  501.             }
  502.             return $color;
  503.         };
  504.         return $callback;
  505.     }
  506.     private function callbackToc()
  507.     {
  508.         $callback = function ($toc) {
  509.             dump($toc);
  510.             return $toc;
  511.         };
  512.         return $callback;
  513.     }
  514.     private function callbackPublisher() {
  515.         $callback = function ($publisher) {
  516.             if ($publisher instanceof Publisher) {
  517.                 $publisher = [
  518.                     'name' => $publisher->getName(),
  519.                     'logo' => ($publisher->getLogo()) ? $publisher->getLogo()->getUrlMethod() : null,
  520.                     'website' => $publisher->getWebsite(),
  521.                     'email' => $publisher->getEmail(),
  522.                     'address' => $publisher->getAddress(),
  523.                     'phone' => $publisher->getPhone(),
  524.                 ];
  525.             } else {
  526.                 $publisher null;
  527.             }
  528.             return $publisher;
  529.         };
  530.         return $callback;
  531.     }
  532. }
  533. class Chapter
  534. {
  535.     public $title;
  536.     public $start;
  537.     public $end;
  538.     public $thumbnail;
  539.     public $pages = [];
  540. }