<?php
/*
* Introduction
*/
$asciiArt = <<<EOT
__ ________ _____ _______ ____ _____
\ \ / / ____/ ____|__ __/ __ \| __ \
\ \ / /| |__ | | | | | | | | |__) |
\ \/ / | __|| | | | | | | | _ /
\ / | |___| |____ | | | |__| | | \ \
\/ |______\_____| |_| \____/|_| \_\
EOT;
/*
* General exception class necessary for scalar type hinting.
*/
class WrongDataException extends Exception
{
private $expectedType;
public function __construct($expectedType,
$message = "Wrong data type. Expected ",
$code = 1,
Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->expectedType = $expectedType;
}
public function __toString()
{
return __CLASS__ . "{$this->code}: {$this->message}" .
"{$this->expectedType}\n" ;
}
}
/*
* Helper class necessary for an appropriate grid view representation.
*/
class PrettyPrinter
{
private $head = [
"col0" => "№",
"col1" => "департамент",
"col2" => "Σрабов",
"col3" => "Σтугриков",
"col4" => "Σкофе",
"col5" => "Σстраниц",
"col6" => "тугриков/страниц"
];
private $foot = [
"col0" => " ",
"col1" => "Итого",
"col2" => NULL,
"col3" => NULL,
"col4" => NULL,
"col5" => NULL,
"col6" => NULL
];
private $trail = [];
private $padder;
private $prettyData = [];
private $printableResult;
public function __construct($inputArr)
{
$this->setPadder();
$this->setHeadTrials();
for($i = 0; $i < count($inputArr); $i++) { $inputArr[$i] = array_merge(["№" => $enum[$i]], $inputArr[$i]); $this->fillPrettyData($inputArr[$i]);
}
$this->setPrintableHeader();
$this->initFooter($inputArr);
$this->setPrintableFoot();
foreach($this->prettyData as $row) {
$this->setPrintableResult($row);
}
}
private function setPrintableHeader()
{
$this->printableResult .= (string)($header);
$this->printableResult .= " ";
}
$this->printableResult .= ("\n" . str_repeat("-", $this->padder) . "\n"); }
private function setHeadTrials()
{
foreach($this->head as $col=>$str) {
}
}
private function setPadder()
{
$func = function($value) {
};
}
private function fillPrettyData($arr)
{
$func = function($value) {
};
$dataLens = [];
foreach($temp as $len1=>$len2) {
$len = abs($len1 - $len2); }
$dataLens);
}
private function setPrintableResult($arr)
{
foreach($arr as $data=>$padding) {
$res = (string)$data . (string)$padding;
$this->printableResult .= $res;
$this->printableResult .= " ";
}
$this->printableResult .= ("\n" . str_repeat("-", $this->padder) . "\n"); }
public function __toString()
{
return $this->printableResult;
}
private function initFooter($arr)
{
$allWorkers = 0;
$allCoffee = 0;
$allSalary = 0;
$allPages = 0;
$allAvg = [];
foreach($arr as $subarr) {
$allWorkers += $cur_vals[2];
$allSalary += $cur_vals[3];
$allCoffee += $cur_vals[4];
$allPages += $cur_vals[5];
}
$this->foot["col2"] = $allWorkers;
$this->foot["col3"] = $allSalary;
$this->foot["col4"] = $allCoffee;
$this->foot["col5"] = $allPages;
}
private function setPrintableFoot()
{
$this->fillPrettyData($this->foot);
}
}
/*
* Helper class necessary for parsing input data and producing associative array.
* Example:
* INPUT: ["9*ме1", "3*ме2"]
* RESULT: ["менеджер" => ["ранг" => "1" "количество" => "9"]], [(int) => ["ранг" => "2" "количество" => "3"]]
* It is possible to add new PCRE => workers relations throug "addNewTypeRel($rel)" method.
* Example: addNewTypeRel(["дв" => "дворник"])
*/
class InitDataParser
{
private $temp = 0;
private $depInit = [];
private $depBoss = [];
private static $reAmt = "(\d+)(\×)";
private static $reType = ["ан", "ин", "ма", "ме"];
private static $reRank = "(\d+)";
/*
* RESULT:
* "/(\d+)(\×)(ан|ин|ма|ме)(\d+)/ui"
*/
private $pattern;
/*
* RESULT:
* "/(\d+)\×)(ан|ин|ма|ме)(\d+)/ui"
*/
private $bossPattern;
private static $typesDispatcher = [
"ан" => "аналитик",
"ин" => "инженер",
"ма" => "маркетолог",
"ме" => "менеджер"
];
public function __construct($inputArr, $boss)
{
$this->initRePatterns();
throw new WrongDataException("array");
}
throw new WrongDataException("string");
}
foreach($inputArr as $single) {
throw new WrongDataException("string");
}
$this->parseInit($single);
if($boss) {
$this->parseBoss($boss);
}
}
}
private function initRePatterns()
{
$this->pattern = "/" . self::$reAmt .
"(" . implode("|", self::$reType) . ")" . self::$reRank . "/ui";
$this->bossPattern = "/" .
"(" . implode("|", self::$reType) . ")" . self::$reRank . "/ui";
}
private function parseInit($shorty)
{
if(preg_match($this->pattern, $shorty, $matches)) { //var_dump($matches);
$amt = $matches[1];
$workType = $matches[3];
$rank = $matches[4];
} else {
throw new Exception("Wrong format.");
}
$this->depInit[self::$typesDispatcher[$workType]] = [
"ранг" => (int)$rank,
"количество" => (int)$amt
];
} else {
$part = [
"ранг" => (int)$rank,
"количество" => (int)$amt
];
/*
* We should somehow get over array_key duplications.
* Therefore following code is going to check the type of array_keys.
* This following checking succids only in case if type is "int".
* Random bottom boundary should guarantee that it would be greater than
* amt of specializiatons (e.g. managers, analysts).
*/
$bottom = rand(100, 10000); if ($this->temp !== $bottom) {
$this->depInit[$bottom] = $part;
} else {
$this->depInit[$bottom + 1] = $part;
}
$this->temp = $bottom;
}
}
private function parseBoss($str)
{
if(preg_match($this->bossPattern, $str, $matches)) { $workType = $matches[1];
$rank = $matches[2];
} else {
throw new Exception("Wrong format.");
}
$this->depBoss = [
"квалификация" => self::$typesDispatcher[$workType],
"ранг" => (int)$rank
];
}
public function getMatchedDepInit()
{
return $this->depInit;
}
public function getMatchedDepBoss()
{
return $this->depBoss;
}
public static function addNewTypeRel($rel)
{
self::$typesDispatcher = array_merge(self::$typesDispatcher, $rel); }
}
/*
* General class for all the employees, holding data and operations general for all subclasses.
*/
abstract class Worker
{
protected $salary;
protected $coffeeAmount;
protected $rank;
protected $bossHonors = [];
protected $workDone;
public function getSalary()
{
return $this->salary + $this->salary * ($this->rank / 100);
}
public function setSalary($num)
{
$this->salary = (int)$num;
} else {
throw new WrongDataException("int");
}
}
public function getCoffee()
{
return $this->coffeeAmount;
}
public function setCoffee($amount)
{
$this->coffeeAmount = (int)$amount;
} else {
throw new WrongDataException("int");
}
}
public function getRank()
{
return $this->rank;
}
public function setRank($num)
{
$this->rank = (int)$num;
} else {
throw new WrongDataException("int");
}
}
public function getWorkResults()
{
return $this->workDone;
}
public function setWorkResults($num)
{
$this->workDone = (int)$num;
} else {
throw new WrongDataException("int");
}
}
public function isBoss()
{
if(!empty($this->bossHonors)) { return true;
}
return false;
}
public function getBossSalary()
{
$baseSalary = $this->salary + $this->salary * ($this->rank / 100);
return $baseSalary + $baseSalary * ($this->bossHonors["зарплата"] / 100);
}
public function getBossCoffee()
{
return $this->coffeeAmount * $this->bossHonors["кофе"];
}
public function getBossWork()
{
return $this->bossHonors["страницы"];
}
public function setBossHonors($honors)
{
$this->bossHonors = $honors;
}
}
class Manager extends Worker
{
}
class Analyst extends Worker
{
}
class Engineer extends Worker
{
}
class Marketer extends Worker
{
}
class Accountant extends Worker
{
}
/*
* Factory class that uses dispatchers to initialize a "Worker" subclasses' object.
*/
class WorkersCreator
{
/*
* Workers' condition's dispatchers.
*/
protected static $commonRanks = [1 => 0, 2 => 25, 3 => 50];
protected static $condsDispatcher = [
"аналитик" =>
[
"зарплата" => 800,
"ранги" => NULL,
"кофе" => 50,
"страницы" => 5
],
"инженер" =>
[
"зарплата" => 200,
"ранги" => NULL,
"кофе" => 5,
"страницы" => 50
],
"менеджер" =>
[
"зарплата" => 500,
"ранги" => NULL,
"кофе" => 20,
"страницы" => 200
],
"маркетолог" =>
[
"зарплата" => 400,
"ранги" => NULL,
"кофе" => 15,
"страницы" => 150
],
];
protected static $clsDispatcher = [
"аналитик" => "Analyst",
"инженер" => "Engineer",
"маркетолог" => "Marketer",
"менеджер" => "Manager",
];
/*
* Boss's honor's dispatcher.
*/
protected static $genHonors = [
"зарплата" => 50,
"кофе" => 2,
"страницы" => 0
];
protected static $bossConds = [];
/*
* Array, populated with "Worker" subclasses' objects.
*/
protected $depWorkers = [];
public function __construct($inDepInit, $inBoss)
{
throw new WrongDataException("array");
}
throw new WrongDataException("string");
}
$parsedWorkers = new InitDataParser($inDepInit, $inBoss);
$depInit = $parsedWorkers->getMatchedDepInit();
$boss = $parsedWorkers->getMatchedDepBoss();
self::initRanks();
$bossConds["привилегии"] = self::$genHonors;
$short = $boss["квалификация"];
$this->initBoss($short);
}
$this->initDepWorkers($depInit);
}
private static function initRanks()
{
foreach(array_keys(self::$condsDispatcher) as $title) { self::$condsDispatcher[$title]["ранги"] = self::$commonRanks;
}
}
private function initDepWorkers($depInit)
{
$cls = self::$clsDispatcher;
$cond = self::$condsDispatcher;
// Explicit and silent converting of ints to previous string value.
// This tricky way helps excluding key duplicates in an array.
$shortName = $temp;
} else {
$temp = $shortName;
}
$rank = $cond[$shortName]["ранги"][$tempRank];
$this->initWorkersClass($cls[$shortName], $cond[$shortName], $rank, $limit);
} else {
throw new Exception("String doesn't match to class");
}
}
}
protected function initWorkersClass($workerCls, $workersCond, $rank, $limit)
{
for($i = 0; $i < $limit; $i++) {
$worker = new $workerCls;
$worker->setSalary($workersCond["зарплата"]);
$worker->setCoffee($workersCond["кофе"]);
$worker->setRank($rank);
$worker->setWorkResults($workersCond["страницы"]);
}
}
public function initBoss($short)
{
$cls = self::$clsDispatcher;
$cond = self::$condsDispatcher;
$bossCls = $cls[$short];
$basicConds = $cond[$short];
} else {
throw new Exception("String doesn't match to class");
}
$boss = new $bossCls;
$boss->setSalary($basicConds["зарплата"]);
$tempRank = self::$bossConds["ранг"];
$boss->setRank($cond[$short]["ранги"][$tempRank]);
$boss->setCoffee($cond[$short]["кофе"]);
$boss->setBossHonors(self::$bossConds["привилегии"]);
}
public static function addClassRel($title, $classname)
{
self::$clsDispatcher[$title] = $classname;
}
public static function addCondRel($title, $cond)
{
self::$condsDispatcher[$title] = $cond;
}
public static function changeSalary($victim, $newVal)
{
self::$condsDispatcher[$victim]["зарплата"] = $newVal;
}
public static function changeBossRank($newVal)
{
self::$bossConds["ранг"] = $newVal;
}
}
/*
* Additional functionality provides ways to generate and fecth statistical data
* (e.g. total department salary).
*/
class Department extends WorkersCreator
{
private $totalSalary = [];
private $totalCoffeeAmount = [];
private $totalWorkDone = [];
private $statData = [];
private static $depName;
function __construct($inDepInit, $inBoss, $depName)
{
throw new WrongDataException("string");
}
self::$depName = $depName;
parent::__construct($inDepInit, $inBoss);
$this->fillTotals();
$this->initStatData();
}
public function fillTotals()
{
foreach($this->depWorkers as $worker) {
throw new WrongDataException("object");
break;
}
if($worker->isBoss()) {
array_push($this->totalSalary, $worker->getBossSalary()); array_push($this->totalCoffeeAmount, $worker->getBossCoffee()); array_push($this->totalWorkDone, $worker->getBossWork()); } else {
array_push($this->totalCoffeeAmount, $worker->getCoffee()); array_push($this->totalWorkDone, $worker->getWorkResults()); array_push($this->totalSalary, $worker->getSalary()); }
}
}
public function getTotalSalary()
{
}
public function getTotalPages()
{
}
public function getTotalCoffee()
{
}
public function countDepWorkers()
{
return count($this->depWorkers); }
public function initStatData()
{
$this->statData["Департамент"] = self::$depName;
$this->statData["Всего рабов"] = $this->countDepWorkers();
$this->statData["Общая з/п"] = $this->getTotalSalary();
$this->statData["Всего кофе"] = $this->getTotalCoffee();
$this->statData["Всего страниц"] = $this->getTotalPages();
$this->statData["Тугриков на страницу"] = number_format(($this->getTotalSalary() / $this->getTotalPages()), 3);
}
public function getStatData()
{
return $this->statData;
}
public function getWorkers()
{
return $this->depWorkers;
}
public static function addNewWorkerType($newWorker)
{
$title = $newWorker[0];
$clsname = $newWorker[1];
$shortTitle = $newWorker[2];
$conds = $newWorker[3];
throw new WrongDataException("string");
}
throw new WrongDataException("array");
}
parent::addClassRel($title, $clsname);
parent::addCondRel($title, $conds);
InitDataParser::addNewTypeRel([$shortTitle => $title]);
}
public static function changeWorkerCond($newConds)
{
$victim = $newConds[0];
$condKey = $newConds[1];
$newVal = $newConds[2];
throw new WrongDataException("string");
}
throw new WrongDataException("int");
}
switch ($condKey) {
case "зарплата":
parent::changeSalary($victim, $newVal);
break;
default:
throw new Exception("Not implemented yet.");
}
}
public function setNewWorkers($arr)
{
$this->depWorkers = $arr;
}
public function __clone()
{
//$this->depName = $this->depName;
$this->totalSalary = [];
$this->totalCoffeeAmount = [];
$this->totalWorkDone = [];
$this->statData = [];
}
public function addWorker($title, $amt, $rank)
{
//var_dump($title);
$cls = parent::$clsDispatcher[$title];
$conds = parent::$condsDispatcher[$title];
$rank = $conds["ранги"][$rank];
//var_dump($rank);
$this->initWorkersClass($cls, $conds, $rank, $amt);
//initWorkersClass($workerCls, $workersCond, $rank, $limit)
}
}
/*
* Testing of general initialization and basic structure.
*/
function testingBasic()
{
$purchaseDepInput = ["9×ме1", "3×ме2", "2×ме3", "2×ма1"];
$purchaseDepBossInput = "ме2";
$purchaseDepName = "покупок";
$saleDepInput = ["12×ме1", "6×ма1", "3×ан1", "2×ан2"];
$saleDepBossInput = "ма2";
$saleDepName = "продаж";
$adsDepInput = ["15×ма1", "10×ма2", "8×ме1", "2×ин1"];
$adsDepBossInput = "ма3";
$adsDepName = "рекламы";
$logisticsDepInput = ["13×ме1", "5×ме2", "5×ин1"];
$logisticsDepBossInput = "ме1";
$logisticsDepName = "логистики";
$purchaseDep = new Department($purchaseDepInput, $purchaseDepBossInput, $purchaseDepName);
$saleDep = new Department($saleDepInput, $saleDepBossInput, $saleDepName);
$adsDep = new Department($adsDepInput, $adsDepBossInput, $adsDepName);
$logisticsDep = new Department($logisticsDepInput, $logisticsDepBossInput, $logisticsDepName);
$purchaseDepData = $purchaseDep->getStatData();
$saleDepData = $saleDep->getStatData();
$adsDepData = $adsDep->getStatData();
$logisticsDepData = $logisticsDep ->getStatData();
$allDeps = [$purchaseDepData, $saleDepData, $adsDepData, $logisticsDepData];
$data = new PrettyPrinter($allDeps);
echo $data;
}
/*
* Testing of addition of new employee and
* testing of changes made to analyst's inital conditions.
*/
function testingAddedChanges()
{
/*
* New Worker type - Accountant. And his/her conditions.
*/
$accConds = [
"зарплата" => 300,
"ранги" => NULL,
"кофе" => 10,
"страницы" => 25
];
$newWorker = ["бухгалтер", "Accountant", "бу", $accConds];
/*
* Let's reduce analysts salary to 100 T.
*/
$crisis = ["аналитик", "зарплата", 700];
$saleDepInputBef = ["12×ме1", "6×ма1", "3×ан1", "2×ан2"];
$saleDepBossInputBef = "ма2";
$saleDepNameBef = "продаж";
$saleDepBef = new Department($saleDepInputBef, $saleDepBossInputBef, $saleDepNameBef);
$before = $saleDepBef->getTotalSalary();
echo "\n";
echo "Тестирование добавления нового рабочего. Бухгалтер: з.п => 300, кол-во => 3\n";
echo "На дворе кризис. Срежем з/п аналитиков до 700 у.е.\n";
echo "\n";
Department::addNewWorkerType($newWorker);
Department::changeWorkerCond($crisis);
$testDepInput = ["12×ме1", "6×ма1", "3×ан1", "2×ан2", "3×бу1"];
$testDepBoss = "ма2";
$testDepName = "тестовый";
$saleDepInput = ["12×ме1", "6×ма1", "3×ан1", "2×ан2"];
$saleDepBossInput = "ма2";
$saleDepName = "продаж";
$testDep = new Department($testDepInput, $testDepBoss, $testDepName);
$saleDep = new Department($saleDepInput, $saleDepBossInput, $saleDepName);
$testDepData = $testDep->getStatData();
$saleDepData = $saleDep->getStatData();
$data = new PrettyPrinter([$testDepData, $saleDepData]);
$newWorkerTest = $testDep->getTotalSalary() - $saleDep->getTotalSalary();
$crisisTest = $saleDepBef->getTotalSalary() - $saleDep->getTotalSalary();
echo $data . "\n";
echo "Результаты разниц до и после.\n";
echo "Верно: 1. 300*3 = 900";
echo " 2. 100*3 + 100*2 + 100*(25/100)*2 = 550\n";
echo "1. Σтугриков (тестовый) - " . "Σтугриков (продаж) " . "= " . "{$newWorkerTest}\n";
echo "2. Σтугриков (до кризиса) - " . "Σтугриков (после) " . "= " . "{$crisisTest}\n";
}
class CrisisModel
{
private $allDeps;
private $newModelWorkers = [];
private $purchaseDep;
private $saleDep;
private $adsDep;
private $logisticsDep;
private $newDeps = [];
private $newStatData = [];
public function __construct($model)
{
$this->initBase();
if($model === 1) {
foreach($this->allDeps as $depWorkers) {
$this->tryModel1($depWorkers);
}
} elseif($model === 2) {
foreach($this->allDeps as $depWorkers) {
$this->tryModel2($depWorkers);
}
} elseif($model === 3) {
//var_dump(count($this->allDeps));
foreach($this->allDeps as $depWorkers) {
$this->tryModel3($depWorkers);
}
}
$this->setNewWorkers($this->newModelWorkers);
$this->setNewStatData();
$this->showModel();
}
private function initBase()
{
$purchaseDepInput = ["9×ме1", "3×ме2", "2×ме3", "2×ма1"];
$purchaseDepBossInput = "ме2";
$purchaseDepName = "покупок";
$saleDepInput = ["12×ме1", "6×ма1", "3×ан1", "2×ан2"];
$saleDepBossInput = "ма2";
$saleDepName = "продаж";
$adsDepInput = ["15×ма1", "10×ма2", "8×ме1", "2×ин1"];
$adsDepBossInput = "ма3";
$adsDepName = "рекламы";
$logisticsDepInput = ["13×ме1", "5×ме2", "5×ин1"];
$logisticsDepBossInput = "ме1";
$logisticsDepName = "логистики";
$this->purchaseDep = new Department($purchaseDepInput, $purchaseDepBossInput, $purchaseDepName);
$this->saleDep = new Department($saleDepInput, $saleDepBossInput, $saleDepName);
$this->adsDep = new Department($adsDepInput, $adsDepBossInput, $adsDepName);
$this->logisticsDep = new Department($logisticsDepInput, $logisticsDepBossInput, $logisticsDepName);
$this->allDeps = [$this->purchaseDep, $this->saleDep, $this->adsDep, $this->logisticsDep];
}
public function tryModel1($dep)
{
$depWorkers = $dep->getWorkers();
$lim = count($depWorkers); $limit = 0;
foreach($depWorkers as $worker) {
$limit++;
}
}
foreach($depWorkers as $worker) {
if(get_class($worker) === "Engineer" && $worker->isBoss() === false) { }
}
}
public function tryModel2($dep)
{
$depWorkers = $dep->getWorkers();
foreach($depWorkers as $worker) {
$worker->setSalary(1100);
$worker->setCoffee(75);
}
}
}
public function tryModel3($dep)
{
$depWorkers = $dep->getWorkers();
$rankOne = [];
$rankTwo = [];
foreach($depWorkers as $worker) {
if(get_class($worker) === "Manager" && $worker->isBoss() === false) { if($worker->getRank() === 0) {
} elseif($worker->getRank() === 25) {
}
}
}
$dep->addWorker("менеджер", $lim1, 2);
$dep->addWorker("менеджер", $lim2, 3);
//var_dump($lim1);
//var_dump($lim2);
//var_dump(count($depWorkers));
//var_dump(count($dep->getWorkers()));
array_push($this->newModelWorkers, $dep->getWorkers()); }
public function setNewWorkers($newArr)
{
$newPD = clone $this->purchaseDep;
$newPD->setNewWorkers($newArr[0]);
$newPD->fillTotals();
$newSD = clone $this->saleDep;
$newSD->setNewWorkers($newArr[1]);
$newSD->fillTotals();
$newAD = clone $this->adsDep;
$newAD->setNewWorkers($newArr[2]);
$newAD->fillTotals();
$newLD = clone $this->logisticsDep;
$newLD->setNewWorkers($newArr[3]);
$newLD->fillTotals();
$this->newDeps = [$newPD, $newSD, $newAD, $newLD];
}
public function setNewStatData()
{
foreach($this->newDeps as $dep) {
$dep->initStatData();
array_push($this->newStatData, $dep->getStatData()); }
}
public function showModel()
{
$data = new PrettyPrinter($this->newStatData);
echo $data;
}
}
echo "{$asciiArt}";
echo "\n\n";
testingBasic();
testingAddedChanges();
echo "\n\n";
echo "Подборка моделей антикризисных ситуаций\n";
echo "Начальные данные вывведены в 1 таблице\n";
echo "\n\n";
$crisisModel1 = new CrisisModel(1);
$crisisModel2 = new CrisisModel(2);
$crisisModel3 = new CrisisModel(3);