Edit via SFTP
  1. <?php namespace ferret;
  2. /*
  3.   HISTORY:
  4.   2021-04-13 split off map-space.php from map.php; renamed map.php to map-piece.php
  5. */
  6. /*::::
  7.   RULES:
  8.   * A Space is a collection of Pieces stored in a private array ($arPc).
  9.   * A Piece is just a copy of a value or object. Writing to a value-Piece won't change the original value.
  10.   It might be useful in the future to change this, but it's complicated and for now not needed.
  11.   METHODS:
  12.   GetPiece() is for when you're sure the Piece exists.
  13.   MakePiece() is for when you're not sure; mostly needed for setting it up.
  14.   HISTORY:
  15.   2021-02-07 created cSpace, probably also cContainer
  16.   2021-02-21 drastically rethought how things work
  17.   2021-02-22
  18.   simplifying: merging cContainer back into cSpace
  19.   created cAutoSpace
  20.   2021-05-22 suddenly realized we can just create the CheckPoint here...
  21.   2021-09-29 tSpaceMaker does not seem to be used; renamed it to verify
  22.   2021-09-30 cAutoSpace now looks completely redundant; not sure what happened. Removing it.
  23. */
  24.  
  25. class cSpace {
  26.  
  27. public function __construct() {
  28. $this->SetCaller(new cStackTrace);
  29. $this->DoPopulate();
  30. }
  31. protected function DoPopulate() : void {} // STUB
  32.  
  33. protected function OnPopulateDone() {
  34. $this->SelfAssess();
  35. $this->IncPopulateCount();
  36. }
  37.  
  38. private $arPc = array();
  39. public function SetPiece(ifPiece $op) { $this->arPc[$op->GetName()] = $op; }
  40. public function GetPiece(string $sName) : ifPiece {
  41.  
  42. //+DEBUG
  43. if (!$this->HasPiece($sName)) {
  44. $htClass = cEnv::BoldIt(get_class($this));
  45. $htName = cEnv::BoldIt($sName);
  46. $htID = cEnv::BoldIt(spl_object_id($this));
  47. $sMsg = "Caller requested Space $htClass (ID $htID) to return Piece $htName, which has not been created.";
  48. $e = new except\cUsage($sMsg);
  49. $e->AddDiagnostic(cEnv::HideDrop('Space object info',$this->DumpLine()));
  50. throw $e;
  51. }
  52. //-DEBUG
  53.  
  54. return $this->arPc[$sName];
  55. }
  56. public function HasPiece(string $sName) : bool {
  57. return array_key_exists($sName,$this->arPc);
  58. }
  59. public function MakePiece(string $sName, string $sClassPiece=NULL, $sClassContent=NULL) : ifPiece {
  60. if ($this->HasPiece($sName)) {
  61. $op = $this->arPc[$sName];
  62. } else {
  63. $op = $this->CreatePiece($sName,$sClassPiece,$sClassContent);
  64. }
  65. return $op;
  66. }
  67. protected function CreatePiece(string $sName, string $sClassPiece=NULL, $sClassContent=NULL) : ifPiece {
  68. $sClass = $this->CheckPieceClass($sClassPiece);
  69. $op = new $sClass($this,$sName,$sClassContent);
  70. $this->arPc[$sName] = $op;
  71.  
  72. return $op;
  73. }
  74. // USAGE: internal
  75. protected function CheckPieceClass(string $sClassPiece=NULL) : string {
  76. if (is_null($sClassPiece)) {
  77. $sClassPiece = cObjectPiece::class; // default Piece class
  78. } elseif (!is_a($sClassPiece,ifPiece::class,TRUE)) {
  79. $htClassThis = cEnv::BoldIt(get_class($this));
  80. $htClassPiece = cEnv::BoldIt($sClassPiece);
  81. $sMsg = "a class $htClassThis object is trying to make a class $htClassPiece Piece, which is not a Piece class.";
  82. $e = new except\cUsage($sMsg);
  83. throw $e;
  84. }
  85. return $sClassPiece;
  86. }
  87. // TODO: 2021-08-29 See if this is still being used.
  88. public function CopyPieces(cSpace $os) {
  89. foreach ($os->arPc as $sName => $oPiece) {
  90. $this->SetPiece($oPiece);
  91. }
  92. }
  93.  
  94. // ACTION: throw an exception if the named piece has not been provisioned
  95. public function NeedPiece(string $sName) {
  96. $sBase = "Piece '$sName' is needed but has not been ";
  97. if ($this->HasPiece($sName)) {
  98. if (!$this->GetPiece($sName)->HasIt()) {
  99. $e = new except\cDebug($sBase.' provisioned.');
  100. throw $e;
  101. }
  102. } else {
  103. $e = new except\cDebug($sBase.' created.');
  104. $scSpace = get_class($this);
  105. $htCaller = $this->GetCaller()->DumpHTML();
  106. $e->AddDiagnostic("Space class <b>$scSpace</b> was created by $htCaller");
  107. throw $e;
  108. }
  109. }
  110.  
  111. // -- REGISTRY -- //
  112. // ++ IDE ++ //
  113.  
  114. private $oCaller = NULL;
  115. protected function SetCaller(cStackTrace $o) { $this->oCaller = $o; }
  116. public function GetCaller() : cStackTrace {
  117. if (is_null($this->oCaller)) {
  118. $sClass = get_class($this);
  119. $sError = "SetCaller() is not being called from constructor in $sClass.";
  120. $e = new except\cInternal($sError);
  121. throw $e;
  122. }
  123. return $this->oCaller;
  124. }
  125. public function Dump() : string { throw new \exception('2021-09-05 Call DumpLine() or DumpFull().'); }
  126. public function DumpLine() : string {
  127. $htClass = cEnv::BoldIt(get_class($this));
  128. $htID = cEnv::BoldIt(spl_object_id($this));
  129. $htPcs = cEnv::BoldIt(count($this->arPc));
  130. $nHas = 0;
  131. foreach ($this->arPc as $sName => $oPiece) {
  132. if ($oPiece->HasIt()) {
  133. $nHas++;
  134. }
  135. }
  136. $htHas = cEnv::BoldIt($nHas);
  137. return "CLASS $htClass (ID $htID) has $htPcs with $htHas filled.";
  138. }
  139. public function DumpFull() : string {
  140. $sClass = get_class($this);
  141. $idThis = spl_object_id($this);
  142.  
  143. if (count($this->arPc) > 0) {
  144. $htPcDesc = 'contains these Pieces:';
  145. $htPieces = cEnv::OpenList();
  146. foreach ($this->arPc as $sName => $oPiece) {
  147. #htLine = "[$sName] ".cEnv::RightArrow().$oPiece->DumpLine();
  148. $htLine = $this->DumpPiece($sName,$oPiece);
  149. $htPieces .= cEnv::ListItem($htLine);
  150. }
  151. $htPieces .= cEnv::ShutList();
  152. } else {
  153. $htPcDesc = 'contains NO Pieces';
  154. $htPieces = '';
  155. }
  156.  
  157. $htCaller = $this->GetCaller()->RenderTrace();
  158. $sHere = __LINE__.'@'.__FILE__;
  159. $htFrom = $this->GetCaller()->RenderCalledFrom();
  160.  
  161. $sSummary = "Space class ".cEnv::BoldIt($sClass)." (OID: $idThis)";
  162. $sDetails =
  163. cEnv::OpenList()
  164. .cEnv::ListItem('on init - '.$this->AssessedInfo())
  165. .cEnv::ListItem($htPcDesc) . $htPieces
  166. .cEnv::ListItem("and was called from this stack: $htCaller")
  167. .cEnv::SmallIt(cEnv::ItalIt("$sHere from $htFrom"))
  168. .cEnv::ShutList()
  169. #.cEnv::HorizLine()
  170. ;
  171. #$out = cEnv::HideDrop($sSummary,cEnv::BoxIt($sDetails));
  172. $sBoxDetails = cEnv::BoxIt($sDetails);
  173. $out = cEnv::HideDrop($sSummary,$sBoxDetails);
  174. return $out;
  175. }
  176. protected function DumpPiece(string $sName, cThingHolder $oPiece) : string {
  177. return "[$sName] ".cEnv::RightArrow().$oPiece->DumpLine();
  178. }
  179.  
  180. private int $nInitObj = 0;
  181. static private int $nInitCls = 0;
  182. private int $nPcs;
  183. private int $nHas;
  184. protected function IncPopulateCount() { $this->nInitObj++; self::$nInitCls++; }
  185. public function SelfAssess() {
  186. if (self::$nInitCls > 1) {
  187. $s = 'Repopulating an already-populated Space';
  188. $e = new except\cInternal($s);
  189. $e->AddDiagnostic($this->DumpFull());
  190. throw $e;
  191. }
  192.  
  193. $nHas = 0;
  194. $nPcs = 0;
  195. foreach ($this->arPc as $sName => $oPiece) {
  196. if ($oPiece->HasIt()) {
  197. $nHas++;
  198. }
  199. $nPcs++;
  200. }
  201. $this->nPcs = $nPcs;
  202. $this->nHas = $nHas;
  203. }
  204. protected function AssessedInfo() : string {
  205. return
  206. 'populated count: '
  207. .$this->nInitObj.' (object), '
  208. .self::$nInitCls.' (class); '
  209. .'pieces: '
  210. .$this->nPcs.' (total), '
  211. .$this->nHas.' (filled)'
  212. ;
  213. }
  214.  
  215. // -- IDE -- //
  216. }
  217. /*::::
  218.   THINKING: all classes store a local pointer to the Space
  219.   "Base" is the common code.
  220.   "Maker" is the code for objects which create a Space.
  221.   "User" is the code for objects which receive a Space created elsewhere.
  222.   Space-setting method is _Space() instead of SetSpace() so as not to imply GetSpace(),
  223.   and to make it clear it's for internal use only.
  224.   HOSTING: must implement:
  225.   * protected function OnSpace() : void {}
  226.   PURPOSE: for each Space-using object to add its own stuff to the Space
  227. */
  228. trait tSpaceBase {
  229.  
  230. private $oSpace = NULL;
  231. public function Space() : cSpace {
  232. $this->RequireSpace(); // fail informatively
  233. return $this->oSpace;
  234. }
  235. protected function _Space(cSpace $o) {
  236. $this->oSpace = $o;
  237. if (method_exists($this,'OnSpace')) {
  238. $this->OnSpace();
  239. } else {
  240. $sClass = get_class($this);
  241. $sError = <<<__END__
  242. <br>Host class '$sClass' must implement this method:<ul><li><code>protected function OnSpace() : void</code></li></ul>
  243. __END__;
  244. $e = new except\cUsage($sError);
  245. $e->AddDiagnostic('Needed by <b>'.__TRAIT__.'</b> in <b>'.__FILE__.'</b>, first used in class <b>'.__CLASS__.'</b>.');
  246. throw $e;
  247. }
  248. }
  249. public function RequireSpace() : void {
  250. if (is_null($this->oSpace)) {
  251. $sClass = get_class($this);
  252. $sMsg = "Space object is needed in $sClass, but it has not been set.";
  253. $e = new except\cInternal($sMsg);
  254. $e->AddDiagnostic('Trait <b>'.__TRAIT__.'</b> is first used in class <b>'.__CLASS__.'</b>.');
  255. throw $e;
  256. }
  257. }
  258. }
  259. /*::::
  260.   HOSTING:
  261.   call $this->_Space(cSpace) in constructor
  262.   MODEL:
  263.   public function __construct(\ferret\cSpace $o) { $this->_Space($o); }
  264. */
  265. trait tSpaceUser { use tSpaceBase; }
  266.  
  267. // ++ DISCARDED ++ //
  268.  
  269. /*::::
  270.   HOSTING:
  271.   CALL OnSpaceNeeded() before Space() is needed (e.g. in constructor)
  272.   CEMENT SpaceClass()
  273. */
  274. trait tSpaceMaker_DISUSED_20210929 {
  275. use tSpaceBase;
  276.  
  277. // ++ CONFIG ++ //
  278.  
  279. abstract protected function SpaceClass() : string;
  280.  
  281. // -- CONFIG -- //
  282. // ++ SETUP ++ //
  283.  
  284. // DEFAULT
  285. public function OnSpaceNeeded() : void { $this->MakeSpace(); }
  286. /*----
  287.   ACTION: creates the Space object
  288.   HISTORY:
  289.   2021-02-21 rewriting
  290.   */
  291. protected function MakeSpace() : void {
  292. $sClassSpace = $this->SpaceClass();
  293. $sClassUser = get_class($this);
  294. $oSpace = \ferret\cSpace::FetchObject($sClassSpace,$sClassUser);
  295. $this->_Space($oSpace);
  296. }
  297. /* 2021-08-27 old
  298.   protected function MakeSpace() : void {
  299.   $sClassSpace = $this->SpaceClass();
  300.   $oSpace = new $sClassSpace();
  301.   $this->_Space($oSpace);
  302.   }
  303.   */
  304.  
  305. // -- SETUP -- //
  306. }
  307.