Skip to content

Instantly share code, notes, and snippets.

@defrindr
Last active August 12, 2023 03:01
Show Gist options
  • Save defrindr/328f649e8a68393b019c343e5a9accd6 to your computer and use it in GitHub Desktop.
Save defrindr/328f649e8a68393b019c343e5a9accd6 to your computer and use it in GitHub Desktop.
Generate Class Diagram Automatically (CODE IGNITER 3)

How To Use

  1. Copy dan paste file tersebut kedalam controller
  2. Tambahkan List dari controller, dan model yang ingin dibuat UML
  3. Jalankan controller
  4. File Akan otomatis ter-generate
  5. Install plugin PlantUML di VSCODE
  6. Install PlantUML dan gravizz
brew install graphviz
<?php
class extractor extends CI_Controller
{
private $workflow = false;
private $exception_ucwords = ["oa_helper"];
private $sanitized = ["'", '"', ",", " ", ";", "(", ")"];
public function getRealpath($type, $name)
{
$dirs = [
"helper" => APPPATH . "helpers/",
"model" => APPPATH . "models/",
"library" => APPPATH . "libraries/",
"controller" => APPPATH . "controllers/",
];
$path = $dirs[$type] . $name . ".php";
return realpath($path);
}
public function generateUmlClass($type, $name, $reference = null)
{
$uml = "";
$realpath = $this->getRealpath($type, $name);
if (!$realpath) {
// var_dump("$name : File not found");
return;
}
// if file not included
if (!in_array($realpath, get_included_files())) {
// var_dump($realpath);
// var_dump(get_included_files());
// die;
require_once $realpath;
// ob_clean();
}
if (!class_exists($name)) {
// var_dump("$name : Class not found");
return;
}
// get methods and properties
$class = new ReflectionClass($name);
$methods = $class->getMethods();
$properties = $class->getProperties();
// get extends and implements
$extends = $class->getParentClass();
$implements = $class->getInterfaces();
if ($extends) {
$extends = $extends->getName();
} else {
$extends = "";
}
if ($extends == "Tte_msuratworkflow") {
if ($this->workflow == false) {
$this->workflow = true;
$uml .= $this->generateUmlClass("model", "Tte_msuratworkflow");
}
}
if ($implements) {
$implements = implode(",", $implements);
} else {
$implements = "";
}
// get class name
$className = "class " . $class->getName();
// combine class name and extends
if ($extends) {
$className = $className . " extends " . $extends;
}
if ($implements) {
$className = $className . " implements " . $implements;
}
$uml .= "$className {\n";
if ($reference) {
$value_load = $this->fetchLoaderFromFile($reference);
$class_information = array_map([$this, 'parseClassInformation'], $value_load);
$loaders = [];
foreach ($class_information as $information) {
foreach ($information['class'] as $class) {
$loaders[] = [
// type
$information['type'],
// class
$class,
];
}
}
$loaders = array_map("unserialize", array_unique(array_map("serialize", $loaders)));
$loaders = array_map("unserialize", array_unique(array_map("serialize", $loaders)));
if (!empty($loaders)) {
foreach ($loaders as $value) {
// $uml .= "\t #$value[1]:$value[0] = new $value[0]()\n";
$uml .= "\t #$value[0] $value[1]\n";
}
}
}
// get properties
if (!empty($properties)) {
// php7.2 get type data from property
foreach ($properties as $property) {
$property_name = $property->getName();
// check visibility
if ($property->isPrivate()) {
$property_visibility = "-";
} elseif ($property->isProtected()) {
$property_visibility = "#";
} else {
$property_visibility = "+";
}
$uml .= "\t $property_visibility$property_name\n";
}
}
$note = "";
// get methods
if (!empty($methods)) {
foreach ($methods as $method) {
// get arguments
$args = [];
$params = $method->getParameters();
foreach ($params as $param) {
$args[] = $param->getName();
}
// get return type of method
$return = $method->getReturnType() ? $method->getReturnType()->getName() . " " : "";
// check visibility
$visibility = "";
if ($method->isPrivate()) {
$visibility = "-";
} else if ($method->isProtected()) {
$visibility = "#";
} else if ($method->isPublic()) {
$visibility = "+";
}
$method_name = $method->getName();
if($method_name == "load") continue;
if (count($args) > 6) {
$uml .= "\t $visibility" . $return . $method_name . "() \n";
// add note if method has more than 6 arguments
$note .= "\tnote right of $name::$method_name\n";
$note .= "\t\tmethod has more than 6 arguments\n";
$note .= "\tend note\n";
} else {
$uml .= "\t $visibility" . $return . $method_name . "(" . implode(", ", $args) . ") \n"; //disable argument
}
}
}
$uml .= "}\n";
$uml .= $note;
return $uml;
}
/**
* Mendapatkan baris yang mengandung kata load dari sebuah file, kecuali load view
*
* @param string $filepath Path menuju file yang ingin di extract menjadi class diagram
* @return array
*/
function fetchLoaderFromFile($filepath)
{
$value_plain = file_get_contents($filepath);
// Mengambil seluruh loader pada file
$value_load = preg_grep("/load\->/", explode("\n", $value_plain));
// Mengecualikan load untuk view
$value_load = preg_grep("/load\->view/", $value_load, PREG_GREP_INVERT);
return $value_load;
}
/**
* Fungsi digunakan untuk melakukan ekstraksi terhadap variable
* pada sebuah loader
*/
function convertStringToListClass($string, $prefix, $suffix)
{
$dependencies_string = explode($prefix, $string)[1];
$dependencies_string = explode($suffix, $dependencies_string)[0];
$sanitized = $this->sanitized;
// Remove the parentheses and split by ','
$dependencies = explode(',', $dependencies_string);
foreach ($dependencies as $index => $value) {
// menghapus double quote, single quote dan spasi
$value = trim(str_replace($sanitized, '', $value));
// mengubah class menjadi ucword, mengikuti standart penamaan pada CI3
if (!in_array($value, $this->exception_ucwords)) {
$value = ucwords($value);
}
$dependencies[$index] = $value;
}
return $dependencies;
}
/**
* Mengubah string menjadi list array
*
* @param string $line baris kode yang akan di parse
* @return array mengembalikan array dengan kunci ```type``` dan ```class```
*/
function parseClassInformation($line)
{
// convert $this->CI to $this
$line = str_replace('$this->CI', '$this', $line);
$dependencies = explode("->", $line)[2];
$type = explode("(", $dependencies)[0];
$contain_string_array = strpos(strtolower($line), 'array');
$contain_array_symbol = strpos(strtolower($line), '[');
if (is_int($contain_string_array)) {
// Fungsi dibawah untuk meng-handle load model
// dengan cara seperti berikut:
// $this->load->model(array('Msurat_suratkeluar', 'Muser', 'Msurat', 'Mgroup', 'Tte_msurat'));
$dependencies = $this->convertStringToListClass($line, "array(", ")");
} else if (is_int($contain_array_symbol)) {
// Fungsi dibawah untuk meng-handle load model
// dengan cara seperti berikut:
// $this->load->model(['Msurat_suratkeluar', 'Muser', 'Msurat', 'Mgroup', 'Tte_msurat']);
$dependencies = $this->convertStringToListClass($line, "[", "]");
} else {
// Fungsi dibawah untuk meng-handle load model
// dengan cara seperti berikut:
// $this->load->model('Msurat_suratkeluar');
// $this->load->model('Msurat_suratkeluar', 'model');
$dependencies = explode("(", $dependencies)[1]; // 'Msurat_suratkeluar');
$dependencies = explode(")", $dependencies)[0]; // 'Msurat_suratkeluar'
$dependencies = explode(",", $dependencies)[0]; // 'Msurat_suratkeluar'
$dependencies = str_replace($this->sanitized, "", $dependencies); // Msurat_suratkeluar
// mengubah class menjadi ucword, mengikuti standart penamaan pada CI3
if (!in_array($dependencies, $this->exception_ucwords)) {
$dependencies = ucwords($dependencies);
}
$dependencies = [$dependencies]; // ubah menjadi array
}
return [
"type" => $type,
"class" => $dependencies
];
}
private function generateUml($list_components, $fileName)
{
ob_start();
// Variable untuk menampung seluruh file
$list_files = [];
foreach ($list_components as $component) {
$filepath = $this->getRealpath($component[0], $component[1]);
// Matikan program ketika file tidak dapat ditemukan
if (!$filepath) die("404: File {$component[1]} tidak ditemukan");
$list_files[] = $filepath;
}
$list_loaders = [];
foreach ($list_files as $value) {
$class_loadstring = $this->fetchLoaderFromFile($value);
$class_information = array_map([$this, 'parseClassInformation'], $class_loadstring);
$loaders = [];
foreach ($class_information as $information) {
foreach ($information['class'] as $class) {
$loaders[] = [
// type
$information['type'],
// class
$class,
// reference
null // default reference is null
];
}
}
$loaders = array_map("unserialize", array_unique(array_map("serialize", $loaders)));
$list_loaders[] = $loaders;
}
foreach ($list_components as $key => $component) {
$component_with_null = array_merge($component, [null]);
$component_type = $component_with_null[0];
$component_class = $component_with_null[1];
$list_loaders = array_map(function ($dependencies) use ($component_type, $component_class) {
// Melakukan pengecekan apabila terdapat self reference, maka ubah nilai menjadi null
$dependencies = array_map(function ($dependency) use ($component_type, $component_class) {
$dependency_type = $dependency[0];
$dependency_class = $dependency[0];
// Kembalikan nilai null apabila terindinkasi self refenrece
if ($dependency_type == $component_type && $dependency_class == $component_class) return null;
return $dependency;
}, $dependencies);
// menghapus nilai null
$dependencies = array_filter($dependencies);
return $dependencies;
}, $list_loaders);
$list_loaders[$key][] = array_merge($component, [$list_files[$key]]);
}
// flatten array
// var_dump($list_loaders);die;
$flat_loaders = array_reduce($list_loaders, 'array_merge', array());
// menghapus data duplikat
$flat_loaders = array_map("unserialize", array_unique(array_map("serialize", $flat_loaders)));
$uml = "@startuml {$fileName}\n";
// initial class
foreach ($flat_loaders as $value) {
$uml .= $this->generateUmlClass($value[0], $value[1], $value[2]);
}
$uml .= "@enduml\n";
// create or replace file
$path = FCPATH . "out/diagrams/{$fileName}.puml";
$file = fopen($path, "w") or die("Unable to open file!");
fwrite($file, $uml);
fclose($file);
ob_clean();
}
public function index()
{
$list_of_uml = [
// "Surat Masuk Icon" => [
// ["controller", "Suratmasukiconplus"],
// // ["model", "Iip_msuratmasuk"],
// // ["model", "Msurat_suratkeluariconplus"],
// ],
// "Surat Keluar Icon" => [
// ["controller", "Suratkeluariconplus"],
// // ["model", "Msurat_suratkeluariconplus"],
// ],
"Menu Integrasi" => [
["controller", "Integrasi"],
// ["model", "Iip_confsekretaris"],
// ["library", "Plnintegration"],
],
// "Iconplus_Service" => [
// // ["library", "Iconplus_integration"],
// ["controller", "integration/Iconplus"],
// ["controller", "integration/Provider"],
// ["controller", "integration/Sync"],
// // ["model", "Iip_maddress_book"],
// // ["model", "Iip_marea"],
// // ["model", "Iip_mcompany"],
// // ["model", "Iip_morganization"],
// // ["model", "Iip_msuratmasuk"],
// ]
// untuk membagi uml agar menjadi tidak terlalu kompleks
// anda dapat memecahnya menjadi array baru
// [],
];
foreach ($list_of_uml as $fileName => $value) {
// check if file exist
// if (!file_exists(FCPATH . "/diagrams/{$value[0][1]}.puml")) {
try {
$this->workflow = false;
//code...
$this->generateUml($value, $fileName);
echo "$fileName. Generate UML {$value[0][1]} Success\n<br/>";
} catch (\Throwable $th) {
// var_dump($value[0][1]);
// die;
//throw $th;
}
// }
}
}
}
@startuml Iconplus_Service
class Iconplus_integration {
+CI
+IPADDRESS
+APPID
+SHAREDKEY
+__construct()
+getToken()
+request(url, data, method)
+parseResponse(response)
+userAms(idUnit)
+pokokKegiatan(idUnit)
+klasifikasi(idUnit)
+allSuratMasuk(lastdate)
+allOrganization()
+allOrganizationDb()
+perusahaan(idUnit)
+perusahaanDb(idUnit)
+area(idCompany)
+areaDb(idCompany)
+addressBook(personName, idUnit, companyCode, businessAreaCode)
+downloadLampiran(nomorSurat, namaLampiran, lampiranPath)
+cekSuratMasuk(nomorSurat, orgCode, updated_date)
+detailSuratMasuk(nomorSurat)
+kirimSuratMasuk(objects)
}
class Iip_morganization extends CI_Model {
+sync(data)
+all()
+__construct()
+__get(key)
}
class Iip_mcompany extends CI_Model {
+sync(data)
+all(idUnit)
+__construct()
+__get(key)
}
class Iip_marea extends CI_Model {
+sync(data)
+all(idCompany)
+__construct()
+__get(key)
}
class Iip_maddress_book extends CI_Model {
+sync(data)
+all()
+__construct()
+__get(key)
}
class Iip_msuratmasuk extends CI_Model {
+__construct()
+sync(data, dataAkses)
+findByNomorSurat(nomor)
+all()
+getAllCount()
+tracking_disposisi(period, myprofile)
+show_tracking_disposisi(nomor_surat, myprofile)
+paginate(period, myprofile)
+getLastData()
+getLastDateInserted()
+can_disposisi(no_surat, myprofile)
+first_hand_disposition(no_surat, myprofile)
+disposisi()
+__get(key)
}
note right of Iip_msuratmasuk::disposisi
method has more than 6 arguments
end note
class Muser extends CI_Model {
+__construct()
+getmyprofile()
+getprofile_by_posid(posid)
+getprofile_by_para(children_group_id)
-generate_query_profile(qwhere)
+getprofile_by_attr(colname, value)
+getprofile_by_nid(nid, all_array)
+__get(key)
}
@enduml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment