<pman>Intégration de la branche dev74
This commit is contained in:
		
						commit
						982e313342
					
				
							
								
								
									
										1
									
								
								.idea/nulib-base.iml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								.idea/nulib-base.iml
									
									
									
										generated
									
									
									
								
							| @ -4,6 +4,7 @@ | |||||||
|     <content url="file://$MODULE_DIR$"> |     <content url="file://$MODULE_DIR$"> | ||||||
|       <sourceFolder url="file://$MODULE_DIR$/php/src" isTestSource="false" packagePrefix="nulib\" /> |       <sourceFolder url="file://$MODULE_DIR$/php/src" isTestSource="false" packagePrefix="nulib\" /> | ||||||
|       <sourceFolder url="file://$MODULE_DIR$/php/tests" isTestSource="true" packagePrefix="nulib\" /> |       <sourceFolder url="file://$MODULE_DIR$/php/tests" isTestSource="true" packagePrefix="nulib\" /> | ||||||
|  |       <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" /> | ||||||
|       <excludeFolder url="file://$MODULE_DIR$/php/vendor" /> |       <excludeFolder url="file://$MODULE_DIR$/php/vendor" /> | ||||||
|     </content> |     </content> | ||||||
|     <orderEntry type="inheritedJdk" /> |     <orderEntry type="inheritedJdk" /> | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								.idea/php-test-framework.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.idea/php-test-framework.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="PhpTestFrameworkVersionCache"> | ||||||
|  |     <tools_cache> | ||||||
|  |       <tool tool_name="PHPUnit"> | ||||||
|  |         <cache> | ||||||
|  |           <versions> | ||||||
|  |             <info id="Local/php/vendor/autoload.php" version="9.6.23" /> | ||||||
|  |           </versions> | ||||||
|  |         </cache> | ||||||
|  |       </tool> | ||||||
|  |     </tools_cache> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										12
									
								
								.idea/php.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								.idea/php.xml
									
									
									
										generated
									
									
									
								
							| @ -1,5 +1,10 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project version="4"> | <project version="4"> | ||||||
|  |   <component name="MessDetector"> | ||||||
|  |     <phpmd_settings> | ||||||
|  |       <phpmd_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="30000" /> | ||||||
|  |     </phpmd_settings> | ||||||
|  |   </component> | ||||||
|   <component name="MessDetectorOptionsConfiguration"> |   <component name="MessDetectorOptionsConfiguration"> | ||||||
|     <option name="transferred" value="true" /> |     <option name="transferred" value="true" /> | ||||||
|   </component> |   </component> | ||||||
| @ -10,6 +15,11 @@ | |||||||
|     <option name="highlightLevel" value="WARNING" /> |     <option name="highlightLevel" value="WARNING" /> | ||||||
|     <option name="transferred" value="true" /> |     <option name="transferred" value="true" /> | ||||||
|   </component> |   </component> | ||||||
|  |   <component name="PhpCodeSniffer"> | ||||||
|  |     <phpcs_settings> | ||||||
|  |       <phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="30000" /> | ||||||
|  |     </phpcs_settings> | ||||||
|  |   </component> | ||||||
|   <component name="PhpIncludePathManager"> |   <component name="PhpIncludePathManager"> | ||||||
|     <include_path> |     <include_path> | ||||||
|       <path value="$PROJECT_DIR$/php/vendor/symfony/polyfill-ctype" /> |       <path value="$PROJECT_DIR$/php/vendor/symfony/polyfill-ctype" /> | ||||||
| @ -55,7 +65,7 @@ | |||||||
|   </component> |   </component> | ||||||
|   <component name="PhpUnit"> |   <component name="PhpUnit"> | ||||||
|     <phpunit_settings> |     <phpunit_settings> | ||||||
|       <PhpUnitSettings configuration_file_path="$PROJECT_DIR$/php/vendor/sebastian/object-enumerator/phpunit.xml" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" /> |       <PhpUnitSettings custom_loader_path="$PROJECT_DIR$/php/vendor/autoload.php" phpunit_phar_path="" /> | ||||||
|     </phpunit_settings> |     </phpunit_settings> | ||||||
|   </component> |   </component> | ||||||
|   <component name="PsalmOptionsConfiguration"> |   <component name="PsalmOptionsConfiguration"> | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								.idea/phpunit.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.idea/phpunit.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="PHPUnit"> | ||||||
|  |     <option name="directories"> | ||||||
|  |       <list> | ||||||
|  |         <option value="$PROJECT_DIR$/tests" /> | ||||||
|  |       </list> | ||||||
|  |     </option> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										2
									
								
								.udir
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								.udir
									
									
									
									
									
								
							| @ -9,7 +9,7 @@ uinc_options=() | |||||||
| uinc_args=() | uinc_args=() | ||||||
| preconfig_scripts=() | preconfig_scripts=() | ||||||
| configure_variables=(dest) | configure_variables=(dest) | ||||||
| configure_dest_for=(lib/profile.d/nulib-base) | configure_dest_for=(lib/profile.d/nulib) | ||||||
| config_scripts=(lib/uinst/conf) | config_scripts=(lib/uinst/conf) | ||||||
| install_profiles=true | install_profiles=true | ||||||
| profiledir=lib/profile.d | profiledir=lib/profile.d | ||||||
|  | |||||||
| @ -38,6 +38,8 @@ echo "commit=$commit" | |||||||
| # reprendre la valeur affichée par la précédente commande | # reprendre la valeur affichée par la précédente commande | ||||||
| commit=XXX | commit=XXX | ||||||
| 
 | 
 | ||||||
|  | pu | ||||||
|  | 
 | ||||||
| git checkout dev74 | git checkout dev74 | ||||||
| 
 | 
 | ||||||
| git cherry-pick "$commit" | git cherry-pick "$commit" | ||||||
|  | |||||||
| @ -138,9 +138,9 @@ class Capacitor implements ITransactor { | |||||||
|     $this->storage->_reset($this->channel, $recreate); |     $this->storage->_reset($this->channel, $recreate); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function charge($item, $func=null, ?array $args=null, ?array &$values=null): int { |   function charge($item, $func=null, ?array $args=null, ?array &$row=null): int { | ||||||
|     if ($this->subChannels !== null) $this->beginTransaction(); |     if ($this->subChannels !== null) $this->beginTransaction(); | ||||||
|     return $this->storage->_charge($this->channel, $item, $func, $args, $values); |     return $this->storage->_charge($this->channel, $item, $func, $args, $row); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function discharge(bool $reset=true): Traversable { |   function discharge(bool $reset=true): Traversable { | ||||||
|  | |||||||
| @ -25,8 +25,6 @@ class CapacitorChannel implements ITransactor { | |||||||
| 
 | 
 | ||||||
|   const EACH_COMMIT_THRESHOLD = 100; |   const EACH_COMMIT_THRESHOLD = 100; | ||||||
| 
 | 
 | ||||||
|   const USE_CACHE = false; |  | ||||||
| 
 |  | ||||||
|   static function verifix_name(?string &$name, ?string &$tableName=null): void { |   static function verifix_name(?string &$name, ?string &$tableName=null): void { | ||||||
|     if ($name !== null) { |     if ($name !== null) { | ||||||
|       $name = strtolower($name); |       $name = strtolower($name); | ||||||
| @ -60,7 +58,6 @@ class CapacitorChannel implements ITransactor { | |||||||
|     $this->tableName = $tableName; |     $this->tableName = $tableName; | ||||||
|     $this->manageTransactions = $manageTransactions ?? static::MANAGE_TRANSACTIONS; |     $this->manageTransactions = $manageTransactions ?? static::MANAGE_TRANSACTIONS; | ||||||
|     $this->eachCommitThreshold = self::verifix_eachCommitThreshold($eachCommitThreshold); |     $this->eachCommitThreshold = self::verifix_eachCommitThreshold($eachCommitThreshold); | ||||||
|     $this->useCache = static::USE_CACHE; |  | ||||||
|     $this->setup = false; |     $this->setup = false; | ||||||
|     $this->created = false; |     $this->created = false; | ||||||
|     $columnDefinitions = $this->COLUMN_DEFINITIONS(); |     $columnDefinitions = $this->COLUMN_DEFINITIONS(); | ||||||
| @ -111,6 +108,8 @@ class CapacitorChannel implements ITransactor { | |||||||
|           $def = strval($def); |           $def = strval($def); | ||||||
|           if (preg_match('/\bprimary\s+key\b/i', $def)) { |           if (preg_match('/\bprimary\s+key\b/i', $def)) { | ||||||
|             $primaryKeys[] = $col; |             $primaryKeys[] = $col; | ||||||
|  |           } elseif ($def === "genserial") { | ||||||
|  |             $primaryKeys[] = $col; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @ -166,23 +165,6 @@ class CapacitorChannel implements ITransactor { | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |  | ||||||
|    * @var bool faut-il passer par le cache pour les requêtes de all(), each() |  | ||||||
|    * et delete()? |  | ||||||
|    * ça peut être nécessaire avec MySQL/MariaDB si on utilise les requêtes non |  | ||||||
|    * bufférisées, et que la fonction manipule la base de données |  | ||||||
|    */ |  | ||||||
|   protected bool $useCache; |  | ||||||
| 
 |  | ||||||
|   function isUseCache(): bool { |  | ||||||
|     return $this->useCache; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function setUseCache(bool $useCache=true): self { |  | ||||||
|     $this->useCache = $useCache; |  | ||||||
|     return $this; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** |   /** | ||||||
|    * initialiser ce channel avant sa première utilisation. |    * initialiser ce channel avant sa première utilisation. | ||||||
|    */ |    */ | ||||||
| @ -255,6 +237,10 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * Retourner la clé primaire par cette méthode est l'unique moyen de |    * Retourner la clé primaire par cette méthode est l'unique moyen de | ||||||
|    * déclencher une mise à jour plutôt qu'une nouvelle création. |    * déclencher une mise à jour plutôt qu'une nouvelle création. | ||||||
|    * |    * | ||||||
|  |    * Bien que ce ne soit pas prévu à la base, si on veut modifier $item dans | ||||||
|  |    * cette méthode pour des raisons pratiques, il suffit de retournerla valeur | ||||||
|  |    * modifiée avec la clé "item" | ||||||
|  |    * | ||||||
|    * Retourner [false] pour annuler le chargement |    * Retourner [false] pour annuler le chargement | ||||||
|    */ |    */ | ||||||
|   function getItemValues($item): ?array { |   function getItemValues($item): ?array { | ||||||
| @ -277,8 +263,8 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * |    * | ||||||
|    * cette méthode doit être utilisée dans {@link self::onUpdate()} |    * cette méthode doit être utilisée dans {@link self::onUpdate()} | ||||||
|    */ |    */ | ||||||
|   function wasRowModified(array $values, array $pvalues): bool { |   function wasRowModified(array $row, array $prow): bool { | ||||||
|     return $values["item__sum_"] !== $pvalues["item__sum_"]; |     return $row["item__sum_"] !== $prow["item__sum_"]; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   final function serialize($item): ?string { |   final function serialize($item): ?string { | ||||||
| @ -309,17 +295,17 @@ class CapacitorChannel implements ITransactor { | |||||||
|     return array_combine($sumCols, [$serial, $sum]); |     return array_combine($sumCols, [$serial, $sum]); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function wasSumModified(string $key, $value, array $pvalues): bool { |   function wasSumModified(string $key, $value, array $prow): bool { | ||||||
|     $sumCol = $this->getSumCols($key)[1]; |     $sumCol = $this->getSumCols($key)[1]; | ||||||
|     $sum = $this->sum(null, $value); |     $sum = $this->sum(null, $value); | ||||||
|     $psum = $pvalues[$sumCol] ?? $this->sum(null, $pvalues[$key] ?? null); |     $psum = $prow[$sumCol] ?? $this->sum(null, $prow[$key] ?? null); | ||||||
|     return $sum !== $psum; |     return $sum !== $psum; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function _wasSumModified(string $key, array $row, array $prow): bool { |   function _wasSumModified(string $key, array $raw, array $praw): bool { | ||||||
|     $sumCol = $this->getSumCols($key)[1]; |     $sumCol = $this->getSumCols($key)[1]; | ||||||
|     $sum = $row[$sumCol] ?? null; |     $sum = $raw[$sumCol] ?? null; | ||||||
|     $psum = $prow[$sumCol] ?? null; |     $psum = $praw[$sumCol] ?? null; | ||||||
|     return $sum !== $psum; |     return $sum !== $psum; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -328,21 +314,21 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * créer un nouvel élément |    * créer un nouvel élément | ||||||
|    * |    * | ||||||
|    * @param mixed $item l'élément à charger |    * @param mixed $item l'élément à charger | ||||||
|    * @param array $values la ligne à créer, calculée à partir de $item et des |    * @param array $row la ligne à créer, calculée à partir de $item et des | ||||||
|    * valeurs retournées par {@link getItemValues()} |    * valeurs retournées par {@link getItemValues()} | ||||||
|    * @return ?array le cas échéant, un tableau non null à merger dans $values et |    * @return ?array le cas échéant, un tableau non null à merger dans $row et | ||||||
|    * utilisé pour provisionner la ligne nouvellement créée. |    * utilisé pour provisionner la ligne nouvellement créée. | ||||||
|    * Retourner [false] pour annuler le chargement (la ligne n'est pas créée) |    * Retourner [false] pour annuler le chargement (la ligne n'est pas créée) | ||||||
|    * |    * | ||||||
|    * Si $item est modifié dans cette méthode, il est possible de le retourner |    * Si $item est modifié dans cette méthode, il est possible de le retourner | ||||||
|    * avec la clé "item" pour mettre à jour la ligne correspondante. |    * avec la clé "item" pour mettre à jour la colonne correspondante. | ||||||
|    * |    * | ||||||
|    * la création ou la mise à jour est uniquement décidée en fonction des |    * la création ou la mise à jour est uniquement décidée en fonction des | ||||||
|    * valeurs calculées par {@link self::getItemValues()}. Bien que cette méthode |    * valeurs calculées par {@link self::getItemValues()}. Bien que cette méthode | ||||||
|    * peut techniquement retourner de nouvelles valeurs pour la clé primaire, ça |    * peut techniquement retourner de nouvelles valeurs pour la clé primaire, ça | ||||||
|    * risque de créer des doublons |    * risque de créer des doublons | ||||||
|    */ |    */ | ||||||
|   function onCreate($item, array $values, ?array $alwaysNull): ?array { |   function onCreate($item, array $row, ?array $alwaysNull): ?array { | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -351,12 +337,12 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * mettre à jour un élément existant |    * mettre à jour un élément existant | ||||||
|    * |    * | ||||||
|    * @param mixed $item l'élément à charger |    * @param mixed $item l'élément à charger | ||||||
|    * @param array $values la nouvelle ligne, calculée à partir de $item et |    * @param array $row la nouvelle ligne, calculée à partir de $item et | ||||||
|    * des valeurs retournées par {@link getItemValues()} |    * des valeurs retournées par {@link getItemValues()} | ||||||
|    * @param array $pvalues la précédente ligne, chargée depuis la base de |    * @param array $prow la précédente ligne, chargée depuis la base de | ||||||
|    * données |    * données | ||||||
|    * @return ?array null s'il ne faut pas mettre à jour la ligne. sinon, ce |    * @return ?array null s'il ne faut pas mettre à jour la ligne. sinon, ce | ||||||
|    * tableau est mergé dans $values puis utilisé pour mettre à jour la ligne |    * tableau est mergé dans $row puis utilisé pour mettre à jour la ligne | ||||||
|    * existante |    * existante | ||||||
|    * Retourner [false] pour annuler le chargement (la ligne n'est pas mise à |    * Retourner [false] pour annuler le chargement (la ligne n'est pas mise à | ||||||
|    * jour) |    * jour) | ||||||
| @ -365,7 +351,7 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * - La clé primaire (il s'agit généralement de "id_") ne peut pas être |    * - La clé primaire (il s'agit généralement de "id_") ne peut pas être | ||||||
|    * modifiée. si elle est retournée, elle est ignorée |    * modifiée. si elle est retournée, elle est ignorée | ||||||
|    */ |    */ | ||||||
|   function onUpdate($item, array $values, array $pvalues): ?array { |   function onUpdate($item, array $row, array $prow): ?array { | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -373,8 +359,8 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * méthode appelée lors du parcours des éléments avec |    * méthode appelée lors du parcours des éléments avec | ||||||
|    * {@link Capacitor::each()} |    * {@link Capacitor::each()} | ||||||
|    * |    * | ||||||
|    * @param mixed $item l'élément courant |    * @param ?array $row la ligne courante. l'élément courant est accessible via | ||||||
|    * @param ?array $values la ligne courante |    * $row["item"] | ||||||
|    * @return ?array le cas échéant, un tableau non null utilisé pour mettre à |    * @return ?array le cas échéant, un tableau non null utilisé pour mettre à | ||||||
|    * jour la ligne courante |    * jour la ligne courante | ||||||
|    * |    * | ||||||
| @ -382,7 +368,7 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * - La clé primaire (il s'agit généralement de "id_") ne peut pas être |    * - La clé primaire (il s'agit généralement de "id_") ne peut pas être | ||||||
|    * modifiée. si elle est retournée, elle est ignorée |    * modifiée. si elle est retournée, elle est ignorée | ||||||
|    */ |    */ | ||||||
|   function onEach($item, array $values): ?array { |   function onEach(array $row): ?array { | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   const onEach = "->".[self::class, "onEach"][1]; |   const onEach = "->".[self::class, "onEach"][1]; | ||||||
| @ -391,11 +377,11 @@ class CapacitorChannel implements ITransactor { | |||||||
|    * méthode appelée lors du parcours des éléments avec |    * méthode appelée lors du parcours des éléments avec | ||||||
|    * {@link Capacitor::delete()} |    * {@link Capacitor::delete()} | ||||||
|    * |    * | ||||||
|    * @param mixed $item l'élément courant |    * @param ?array $row la ligne courante. l'élément courant est accessible via | ||||||
|    * @param ?array $values la ligne courante |    * $row["item"] | ||||||
|    * @return bool true s'il faut supprimer la ligne, false sinon |    * @return bool true s'il faut supprimer la ligne, false sinon | ||||||
|    */ |    */ | ||||||
|   function onDelete($item, array $values): bool { |   function onDelete(array $row): bool { | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   const onDelete = "->".[self::class, "onDelete"][1]; |   const onDelete = "->".[self::class, "onDelete"][1]; | ||||||
| @ -454,8 +440,8 @@ class CapacitorChannel implements ITransactor { | |||||||
|     $this->capacitor->reset($recreate); |     $this->capacitor->reset($recreate); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function charge($item, $func=null, ?array $args=null, ?array &$values=null): int { |   function charge($item, $func=null, ?array $args=null, ?array &$row=null): int { | ||||||
|     return $this->capacitor->charge($item, $func, $args, $values); |     return $this->capacitor->charge($item, $func, $args, $row); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function discharge(bool $reset=true): Traversable { |   function discharge(bool $reset=true): Traversable { | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| <?php | <?php | ||||||
| namespace nulib\db; | namespace nulib\db; | ||||||
| 
 | 
 | ||||||
|  | use nulib\A; | ||||||
| use nulib\cl; | use nulib\cl; | ||||||
| use nulib\db\_private\_migration; | use nulib\db\_private\_migration; | ||||||
| use nulib\php\func; | use nulib\php\func; | ||||||
| @ -32,21 +33,43 @@ abstract class CapacitorStorage { | |||||||
|     return $channel; |     return $channel; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** DOIT être défini dans les classes dérivées */ |   const PRIMARY_KEY_DEFINITION = [ | ||||||
|   const PRIMARY_KEY_DEFINITION = null; |     "id_" => "genserial", | ||||||
|  |   ]; | ||||||
| 
 | 
 | ||||||
|  |   # les définitions sont par défaut pour MariaDB/MySQL
 | ||||||
|   const SERDATA_DEFINITION = "mediumtext"; |   const SERDATA_DEFINITION = "mediumtext"; | ||||||
|   const SERSUM_DEFINITION = "varchar(40)"; |   const SERSUM_DEFINITION = "varchar(40)"; | ||||||
|   const SERTS_DEFINITION = "datetime"; |   const SERTS_DEFINITION = "datetime"; | ||||||
|  |   const GENSERIAL_DEFINITION = "integer primary key auto_increment"; | ||||||
|  |   const GENLIC_DEFINITION = "varchar(80)"; | ||||||
|  |   const GENLIB_DEFINITION = "varchar(255)"; | ||||||
|  |   const GENTEXT_DEFINITION = "mediumtext"; | ||||||
|  |   const GENBOOL_DEFINITION = "integer(1)"; | ||||||
|  |   const GENUUID_DEFINITION = "varchar(36)"; | ||||||
| 
 | 
 | ||||||
|   protected static function sercol($def): string { |   protected static function gencol($def): string { | ||||||
|     if (!is_string($def)) $def = strval($def); |     if (!is_string($def)) $def = strval($def); | ||||||
|  |     $def = trim($def); | ||||||
|  |     $parts = preg_split('/\s+/', $def, 2); | ||||||
|  |     if (count($parts) == 2) { | ||||||
|  |       $def = $parts[0]; | ||||||
|  |       $rest = " $parts[1]"; | ||||||
|  |     } else { | ||||||
|  |       $rest = null; | ||||||
|  |     } | ||||||
|     switch ($def) { |     switch ($def) { | ||||||
|     case "serdata": $def = static::SERDATA_DEFINITION; break; |     case "serdata": $def = static::SERDATA_DEFINITION; break; | ||||||
|     case "sersum": $def = static::SERSUM_DEFINITION; break; |     case "sersum": $def = static::SERSUM_DEFINITION; break; | ||||||
|     case "serts": $def = static::SERTS_DEFINITION; break; |     case "serts": $def = static::SERTS_DEFINITION; break; | ||||||
|  |     case "genserial": $def = static::GENSERIAL_DEFINITION; break; | ||||||
|  |     case "genlic": $def = static::GENLIC_DEFINITION; break; | ||||||
|  |     case "genlib": $def = static::GENLIB_DEFINITION; break; | ||||||
|  |     case "gentext": $def = static::GENTEXT_DEFINITION; break; | ||||||
|  |     case "genbool": $def = static::GENBOOL_DEFINITION; break; | ||||||
|  |     case "genuuid": $def = static::GENUUID_DEFINITION; break; | ||||||
|     } |     } | ||||||
|     return $def; |     return "$def$rest"; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const COLUMN_DEFINITIONS = [ |   const COLUMN_DEFINITIONS = [ | ||||||
| @ -81,7 +104,7 @@ abstract class CapacitorStorage { | |||||||
|                 $mindex++; |                 $mindex++; | ||||||
|               } else { |               } else { | ||||||
|                 if ($mdef) { |                 if ($mdef) { | ||||||
|                   $definitions[$mcol] = self::sercol($mdef); |                   $definitions[$mcol] = self::gencol($mdef); | ||||||
|                 } else { |                 } else { | ||||||
|                   unset($definitions[$mcol]); |                   unset($definitions[$mcol]); | ||||||
|                 } |                 } | ||||||
| @ -92,7 +115,7 @@ abstract class CapacitorStorage { | |||||||
|           $constraints[] = $def; |           $constraints[] = $def; | ||||||
|         } |         } | ||||||
|       } else { |       } else { | ||||||
|         $definitions[$col] = self::sercol($def); |         $definitions[$col] = self::gencol($def); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return cl::merge($definitions, $constraints); |     return cl::merge($definitions, $constraints); | ||||||
| @ -102,9 +125,35 @@ abstract class CapacitorStorage { | |||||||
|     return $channel->getMigration(); |     return $channel->getMigration(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** sérialiser les valeurs qui doivent l'être dans $values */ |   /** sérialiser les valeurs qui doivent l'être dans $row */ | ||||||
|   protected function serialize(CapacitorChannel $channel, ?array $values): ?array { |   protected function serialize(CapacitorChannel $channel, ?array $row): ?array { | ||||||
|     if ($values === null) return null; |     if ($row === null) return null; | ||||||
|  |     $cols = $this->ColumnDefinitions($channel); | ||||||
|  |     $index = 0; | ||||||
|  |     $raw = []; | ||||||
|  |     foreach (array_keys($cols) as $col) { | ||||||
|  |       $key = $col; | ||||||
|  |       if ($key === $index) { | ||||||
|  |         $index++; | ||||||
|  |       } elseif ($channel->isSerialCol($key)) { | ||||||
|  |         [$serialCol, $sumCol] = $channel->getSumCols($key); | ||||||
|  |         if (array_key_exists($key, $row)) { | ||||||
|  |           $sum = $channel->getSum($key, $row[$key]); | ||||||
|  |           $raw[$serialCol] = $sum[$serialCol]; | ||||||
|  |           if (array_key_exists($sumCol, $cols)) { | ||||||
|  |             $raw[$sumCol] = $sum[$sumCol]; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } elseif (array_key_exists($key, $row)) { | ||||||
|  |         $raw[$col] = $row[$key]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return $raw; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** désérialiser les valeurs qui doivent l'être dans $values */ | ||||||
|  |   protected function unserialize(CapacitorChannel $channel, ?array $raw): ?array { | ||||||
|  |     if ($raw === null) return null; | ||||||
|     $cols = $this->ColumnDefinitions($channel); |     $cols = $this->ColumnDefinitions($channel); | ||||||
|     $index = 0; |     $index = 0; | ||||||
|     $row = []; |     $row = []; | ||||||
| @ -112,44 +161,18 @@ abstract class CapacitorStorage { | |||||||
|       $key = $col; |       $key = $col; | ||||||
|       if ($key === $index) { |       if ($key === $index) { | ||||||
|         $index++; |         $index++; | ||||||
|  |       } elseif (!array_key_exists($col, $raw)) { | ||||||
|       } elseif ($channel->isSerialCol($key)) { |       } elseif ($channel->isSerialCol($key)) { | ||||||
|         [$serialCol, $sumCol] = $channel->getSumCols($key); |         $value = $raw[$col]; | ||||||
|         if (array_key_exists($key, $values)) { |         if ($value !== null) $value = $channel->unserialize($value); | ||||||
|           $sum = $channel->getSum($key, $values[$key]); |         $row[$key] = $value; | ||||||
|           $row[$serialCol] = $sum[$serialCol]; |       } else { | ||||||
|           if (array_key_exists($sumCol, $cols)) { |         $row[$key] = $raw[$col]; | ||||||
|             $row[$sumCol] = $sum[$sumCol]; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } elseif (array_key_exists($key, $values)) { |  | ||||||
|         $row[$col] = $values[$key]; |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return $row; |     return $row; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** désérialiser les valeurs qui doivent l'être dans $values */ |  | ||||||
|   protected function unserialize(CapacitorChannel $channel, ?array $row): ?array { |  | ||||||
|     if ($row === null) return null; |  | ||||||
|     $cols = $this->ColumnDefinitions($channel); |  | ||||||
|     $index = 0; |  | ||||||
|     $values = []; |  | ||||||
|     foreach (array_keys($cols) as $col) { |  | ||||||
|       $key = $col; |  | ||||||
|       if ($key === $index) { |  | ||||||
|         $index++; |  | ||||||
|       } elseif (!array_key_exists($col, $row)) { |  | ||||||
|       } elseif ($channel->isSerialCol($key)) { |  | ||||||
|         $value = $row[$col]; |  | ||||||
|         if ($value !== null) $value = $channel->unserialize($value); |  | ||||||
|         $values[$key] = $value; |  | ||||||
|       } else { |  | ||||||
|         $values[$key] = $row[$col]; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return $values; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function getPrimaryKeys(CapacitorChannel $channel): array { |   function getPrimaryKeys(CapacitorChannel $channel): array { | ||||||
|     $primaryKeys = $channel->getPrimaryKeys(); |     $primaryKeys = $channel->getPrimaryKeys(); | ||||||
|     if ($primaryKeys === null) $primaryKeys = ["id_"]; |     if ($primaryKeys === null) $primaryKeys = ["id_"]; | ||||||
| @ -215,6 +238,22 @@ abstract class CapacitorStorage { | |||||||
|     "class_name" => "varchar", |     "class_name" => "varchar", | ||||||
|   ]; |   ]; | ||||||
| 
 | 
 | ||||||
|  |   function channelExists(string $name, ?array &$raw=null): bool { | ||||||
|  |     $raw = $this->db()->one([ | ||||||
|  |       "select", | ||||||
|  |       "from" => static::CHANNELS_TABLE, | ||||||
|  |       "where" => ["name" => $name], | ||||||
|  |     ]); | ||||||
|  |     return $raw !== null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function getChannels(): iterable { | ||||||
|  |     return $this->db()->all([ | ||||||
|  |       "select", | ||||||
|  |       "from" => static::CHANNELS_TABLE, | ||||||
|  |     ]); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   protected function _createChannelsSql(): array { |   protected function _createChannelsSql(): array { | ||||||
|     return [ |     return [ | ||||||
|       "create table if not exists", |       "create table if not exists", | ||||||
| @ -316,7 +355,7 @@ abstract class CapacitorStorage { | |||||||
|    * en fonction du type d'opération: création ou mise à jour |    * en fonction du type d'opération: création ou mise à jour | ||||||
|    * |    * | ||||||
|    * Dans les deux cas, si la fonction retourne un tableau, il est utilisé pour |    * Dans les deux cas, si la fonction retourne un tableau, il est utilisé pour | ||||||
|    * modifier les valeurs insérées/mises à jour. De plus, $values obtient la |    * modifier les valeurs insérées/mises à jour. De plus, $row obtient la | ||||||
|    * valeur finale des données insérées/mises à jour |    * valeur finale des données insérées/mises à jour | ||||||
|    * |    * | ||||||
|    * Si $args est renseigné, il est ajouté aux arguments utilisés pour appeler |    * Si $args est renseigné, il est ajouté aux arguments utilisés pour appeler | ||||||
| @ -327,23 +366,27 @@ abstract class CapacitorStorage { | |||||||
|    * @return int 1 si l'objet a été chargé ou mis à jour, 0 s'il existait |    * @return int 1 si l'objet a été chargé ou mis à jour, 0 s'il existait | ||||||
|    * déjà à l'identique dans le canal |    * déjà à l'identique dans le canal | ||||||
|    */ |    */ | ||||||
|   function _charge(CapacitorChannel $channel, $item, $func, ?array $args, ?array &$values=null): int { |   function _charge(CapacitorChannel $channel, $item, $func, ?array $args, ?array &$row=null): int { | ||||||
|     $this->_create($channel); |     $this->_create($channel); | ||||||
|     $tableName = $channel->getTableName(); |     $tableName = $channel->getTableName(); | ||||||
|     $db = $this->db(); |     $db = $this->db(); | ||||||
|     $args ??= []; |     $args ??= []; | ||||||
| 
 | 
 | ||||||
|     $values = func::call([$channel, "getItemValues"], $item, ...$args); |     $row = func::call([$channel, "getItemValues"], $item, ...$args); | ||||||
|     if ($values === [false]) return 0; |     if ($row === [false]) return 0; | ||||||
| 
 | 
 | ||||||
|     $row = cl::merge( |     if ($row !== null && array_key_exists("item", $row)) { | ||||||
|  |       $item = A::pop($row, "item"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $raw = cl::merge( | ||||||
|       $channel->getSum("item", $item), |       $channel->getSum("item", $item), | ||||||
|       $this->serialize($channel, $values)); |       $this->serialize($channel, $row)); | ||||||
|     $prow = null; |     $praw = null; | ||||||
|     $rowIds = $this->getRowIds($channel, $row, $primaryKeys); |     $rowIds = $this->getRowIds($channel, $raw, $primaryKeys); | ||||||
|     if ($rowIds !== null) { |     if ($rowIds !== null) { | ||||||
|       # modification
 |       # modification
 | ||||||
|       $prow = $db->one([ |       $praw = $db->one([ | ||||||
|         "select", |         "select", | ||||||
|         "from" => $tableName, |         "from" => $tableName, | ||||||
|         "where" => $rowIds, |         "where" => $rowIds, | ||||||
| @ -352,47 +395,47 @@ abstract class CapacitorStorage { | |||||||
| 
 | 
 | ||||||
|     $now = date("Y-m-d H:i:s"); |     $now = date("Y-m-d H:i:s"); | ||||||
|     $insert = null; |     $insert = null; | ||||||
|     if ($prow === null) { |     if ($praw === null) { | ||||||
|       # création
 |       # création
 | ||||||
|       $row = cl::merge($row, [ |       $raw = cl::merge($raw, [ | ||||||
|         "created_" => $now, |         "created_" => $now, | ||||||
|         "modified_" => $now, |         "modified_" => $now, | ||||||
|       ]); |       ]); | ||||||
|       $insert = true; |       $insert = true; | ||||||
|       $initFunc = func::with([$channel, "onCreate"], $args); |       $initFunc = func::with([$channel, "onCreate"], $args); | ||||||
|       $values = $this->unserialize($channel, $row); |       $row = $this->unserialize($channel, $raw); | ||||||
|       $pvalues = null; |       $prow = null; | ||||||
|     } else { |     } else { | ||||||
|       # modification
 |       # modification
 | ||||||
|       # intégrer autant que possible les valeurs de prow dans row, de façon que
 |       # intégrer autant que possible les valeurs de praw dans raw, de façon que
 | ||||||
|       # l'utilisateur puisse voir clairement ce qui a été modifié
 |       # l'utilisateur puisse voir clairement ce qui a été modifié
 | ||||||
|       if ($channel->_wasSumModified("item", $row, $prow)) { |       if ($channel->_wasSumModified("item", $raw, $praw)) { | ||||||
|         $insert = false; |         $insert = false; | ||||||
|         $row = cl::merge($prow, $row, [ |         $raw = cl::merge($praw, $raw, [ | ||||||
|           "modified_" => $now, |           "modified_" => $now, | ||||||
|         ]); |         ]); | ||||||
|       } else { |       } else { | ||||||
|         $row = cl::merge($prow, $row); |         $raw = cl::merge($praw, $raw); | ||||||
|       } |       } | ||||||
|       $initFunc = func::with([$channel, "onUpdate"], $args); |       $initFunc = func::with([$channel, "onUpdate"], $args); | ||||||
|       $values = $this->unserialize($channel, $row); |       $row = $this->unserialize($channel, $raw); | ||||||
|       $pvalues = $this->unserialize($channel, $prow); |       $prow = $this->unserialize($channel, $praw); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     $updates = $initFunc->prependArgs([$item, $values, $pvalues])->invoke(); |     $updates = $initFunc->prependArgs([$item, $row, $prow])->invoke(); | ||||||
|     if ($updates === [false]) return 0; |     if ($updates === [false]) return 0; | ||||||
|     if (is_array($updates) && $updates) { |     if (is_array($updates) && $updates) { | ||||||
|       if ($insert === null) $insert = false; |       if ($insert === null) $insert = false; | ||||||
|       if (!array_key_exists("modified_", $updates)) { |       if (!array_key_exists("modified_", $updates)) { | ||||||
|         $updates["modified_"] = $now; |         $updates["modified_"] = $now; | ||||||
|       } |       } | ||||||
|       $values = cl::merge($values, $updates); |       $row = cl::merge($row, $updates); | ||||||
|       $row = cl::merge($row, $this->serialize($channel, $updates)); |       $raw = cl::merge($raw, $this->serialize($channel, $updates)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ($func !== null) { |     if ($func !== null) { | ||||||
|       $updates = func::with($func) |       $updates = func::with($func) | ||||||
|         ->prependArgs([$item, $values, $pvalues]) |         ->prependArgs([$item, $row, $prow]) | ||||||
|         ->bind($channel) |         ->bind($channel) | ||||||
|         ->invoke(); |         ->invoke(); | ||||||
|       if ($updates === [false]) return 0; |       if ($updates === [false]) return 0; | ||||||
| @ -401,8 +444,8 @@ abstract class CapacitorStorage { | |||||||
|         if (!array_key_exists("modified_", $updates)) { |         if (!array_key_exists("modified_", $updates)) { | ||||||
|           $updates["modified_"] = $now; |           $updates["modified_"] = $now; | ||||||
|         } |         } | ||||||
|         $values = cl::merge($values, $updates); |         $row = cl::merge($row, $updates); | ||||||
|         $row = cl::merge($row, $this->serialize($channel, $updates)); |         $raw = cl::merge($raw, $this->serialize($channel, $updates)); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -421,25 +464,23 @@ abstract class CapacitorStorage { | |||||||
|         $id = $db->exec([ |         $id = $db->exec([ | ||||||
|           "insert", |           "insert", | ||||||
|           "into" => $tableName, |           "into" => $tableName, | ||||||
|           "values" => $row, |           "values" => $raw, | ||||||
|         ]); |         ]); | ||||||
|         if (count($primaryKeys) == 1 && $rowIds === null) { |         if (count($primaryKeys) == 1 && $rowIds === null) { | ||||||
|           # mettre à jour avec l'id généré
 |           # mettre à jour avec l'id généré
 | ||||||
|           $values[$primaryKeys[0]] = $id; |           $row[$primaryKeys[0]] = $id; | ||||||
|         } |         } | ||||||
|         $nbModified = 1; |         $nbModified = 1; | ||||||
|       } else { |       } else { | ||||||
|         # calculer ce qui a changé pour ne mettre à jour que le nécessaire
 |         # calculer ce qui a changé pour ne mettre à jour que le nécessaire
 | ||||||
|         $updates = []; |         $updates = []; | ||||||
|         foreach ($row as $col => $value) { |         foreach ($raw as $col => $value) { | ||||||
|           if (array_key_exists($col, $rowIds)) { |           if (array_key_exists($col, $rowIds)) { | ||||||
|             # ne jamais mettre à jour la clé primaire
 |             # ne jamais mettre à jour la clé primaire
 | ||||||
|             continue; |             continue; | ||||||
|           } |           } | ||||||
|           $pvalue = $prow[$col] ?? null; |           $pvalue = $praw[$col] ?? null; | ||||||
|           if ($value !== ($pvalue)) { |           if ($value !== $pvalue) $updates[$col] = $value; | ||||||
|             $updates[$col] = $value; |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|         if (count($updates) == 1 && array_key_first($updates) == "modified_") { |         if (count($updates) == 1 && array_key_first($updates) == "modified_") { | ||||||
|           # si l'unique modification porte sur la date de modification, alors
 |           # si l'unique modification porte sur la date de modification, alors
 | ||||||
| @ -467,19 +508,22 @@ abstract class CapacitorStorage { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function charge(?string $channel, $item, $func=null, ?array $args=null, ?array &$values=null): int { |   function charge(?string $channel, $item, $func=null, ?array $args=null, ?array &$row=null): int { | ||||||
|     return $this->_charge($this->getChannel($channel), $item, $func, $args, $values); |     return $this->_charge($this->getChannel($channel), $item, $func, $args, $row); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** décharger les données du canal spécifié */ |   /** | ||||||
|  |    * décharger les données du canal spécifié. seul la valeur de $item est | ||||||
|  |    * fournie | ||||||
|  |    */ | ||||||
|   function _discharge(CapacitorChannel $channel, bool $reset=true): Traversable { |   function _discharge(CapacitorChannel $channel, bool $reset=true): Traversable { | ||||||
|     $this->_create($channel); |     $this->_create($channel); | ||||||
|     $rows = $this->db()->all([ |     $raws = $this->db()->all([ | ||||||
|       "select item__", |       "select item__", | ||||||
|       "from" => $channel->getTableName(), |       "from" => $channel->getTableName(), | ||||||
|     ]); |     ]); | ||||||
|     foreach ($rows as $row) { |     foreach ($raws as $raw) { | ||||||
|       yield unserialize($row['item__']); |       yield unserialize($raw['item__']); | ||||||
|     } |     } | ||||||
|     if ($reset) $this->_reset($channel); |     if ($reset) $this->_reset($channel); | ||||||
|   } |   } | ||||||
| @ -548,45 +592,34 @@ abstract class CapacitorStorage { | |||||||
|     if ($filter === null) throw ValueException::null("filter"); |     if ($filter === null) throw ValueException::null("filter"); | ||||||
|     $this->_create($channel); |     $this->_create($channel); | ||||||
|     $this->verifixFilter($channel, $filter); |     $this->verifixFilter($channel, $filter); | ||||||
|     $row = $this->db()->one(cl::merge([ |     $raw = $this->db()->one(cl::merge([ | ||||||
|       "select", |       "select", | ||||||
|       "from" => $channel->getTableName(), |       "from" => $channel->getTableName(), | ||||||
|       "where" => $filter, |       "where" => $filter, | ||||||
|     ], $mergeQuery)); |     ], $mergeQuery)); | ||||||
|     return $this->unserialize($channel, $row); |     return $this->unserialize($channel, $raw); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function one(?string $channel, $filter, ?array $mergeQuery=null): ?array { |   function one(?string $channel, $filter, ?array $mergeQuery=null): ?array { | ||||||
|     return $this->_one($this->getChannel($channel), $filter, $mergeQuery); |     return $this->_one($this->getChannel($channel), $filter, $mergeQuery); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private function _allCached(string $id, CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable { |  | ||||||
|     $this->_create($channel); |  | ||||||
|     $this->verifixFilter($channel, $filter); |  | ||||||
|     $rows = $this->db()->all(cl::merge([ |  | ||||||
|       "select", |  | ||||||
|       "from" => $channel->getTableName(), |  | ||||||
|       "where" => $filter, |  | ||||||
|     ], $mergeQuery), null, $this->getPrimaryKeys($channel)); |  | ||||||
|     if ($channel->isUseCache()) { |  | ||||||
|       $cacheIds = [$id, get_class($channel)]; |  | ||||||
|       cache::get()->resetCached($cacheIds); |  | ||||||
|       $rows = cache::new(null, $cacheIds, function() use ($rows) { |  | ||||||
|         yield from $rows; |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
|     foreach ($rows as $key => $row) { |  | ||||||
|       yield $key => $this->unserialize($channel, $row); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** |   /** | ||||||
|    * obtenir les lignes correspondant au filtre sur le canal spécifié |    * obtenir les lignes correspondant au filtre sur le canal spécifié | ||||||
|    * |    * | ||||||
|    * si $filter n'est pas un tableau, il est transformé en ["id_" => $filter] |    * si $filter n'est pas un tableau, il est transformé en ["id_" => $filter] | ||||||
|    */ |    */ | ||||||
|   function _all(CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable { |   function _all(CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable { | ||||||
|     return $this->_allCached("all", $channel, $filter, $mergeQuery); |     $this->_create($channel); | ||||||
|  |     $this->verifixFilter($channel, $filter); | ||||||
|  |     $raws = $this->db()->all(cl::merge([ | ||||||
|  |       "select", | ||||||
|  |       "from" => $channel->getTableName(), | ||||||
|  |       "where" => $filter, | ||||||
|  |     ], $mergeQuery), null, $this->getPrimaryKeys($channel)); | ||||||
|  |     foreach ($raws as $key => $raw) { | ||||||
|  |       yield $key => $this->unserialize($channel, $raw); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function all(?string $channel, $filter, $mergeQuery=null): Traversable { |   function all(?string $channel, $filter, $mergeQuery=null): Traversable { | ||||||
| @ -622,10 +655,10 @@ abstract class CapacitorStorage { | |||||||
|     $tableName = $channel->getTableName(); |     $tableName = $channel->getTableName(); | ||||||
|     try { |     try { | ||||||
|       $args ??= []; |       $args ??= []; | ||||||
|       $all = $this->_allCached("each", $channel, $filter, $mergeQuery); |       $rows = $this->_all($channel, $filter, $mergeQuery); | ||||||
|       foreach ($all as $values) { |       foreach ($rows as $row) { | ||||||
|         $rowIds = $this->getRowIds($channel, $values); |         $rowIds = $this->getRowIds($channel, $row); | ||||||
|         $updates = $onEach->invoke([$values["item"], $values, ...$args]); |         $updates = $onEach->invoke([$row, ...$args]); | ||||||
|         if (is_array($updates) && $updates) { |         if (is_array($updates) && $updates) { | ||||||
|           if (!array_key_exists("modified_", $updates)) { |           if (!array_key_exists("modified_", $updates)) { | ||||||
|             $updates["modified_"] = date("Y-m-d H:i:s"); |             $updates["modified_"] = date("Y-m-d H:i:s"); | ||||||
| @ -689,10 +722,10 @@ abstract class CapacitorStorage { | |||||||
|     $tableName = $channel->getTableName(); |     $tableName = $channel->getTableName(); | ||||||
|     try { |     try { | ||||||
|       $args ??= []; |       $args ??= []; | ||||||
|       $all = $this->_allCached("delete", $channel, $filter); |       $rows = $this->_all($channel, $filter); | ||||||
|       foreach ($all as $values) { |       foreach ($rows as $row) { | ||||||
|         $rowIds = $this->getRowIds($channel, $values); |         $rowIds = $this->getRowIds($channel, $row); | ||||||
|         $shouldDelete = boolval($onDelete->invoke([$values["item"], $values, ...$args])); |         $shouldDelete = boolval($onDelete->invoke([$row, ...$args])); | ||||||
|         if ($shouldDelete) { |         if ($shouldDelete) { | ||||||
|           $db->exec([ |           $db->exec([ | ||||||
|             "delete", |             "delete", | ||||||
|  | |||||||
| @ -6,7 +6,6 @@ class _update extends _common { | |||||||
|     "prefix" => "?string", |     "prefix" => "?string", | ||||||
|     "table" => "?string", |     "table" => "?string", | ||||||
|     "schema" => "?array", |     "schema" => "?array", | ||||||
|     "cols" => "?array", |  | ||||||
|     "values" => "?array", |     "values" => "?array", | ||||||
|     "where" => "?array", |     "where" => "?array", | ||||||
|     "suffix" => "?string", |     "suffix" => "?string", | ||||||
|  | |||||||
| @ -19,10 +19,6 @@ class MysqlStorage extends CapacitorStorage { | |||||||
|     return $this->db; |     return $this->db; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const PRIMARY_KEY_DEFINITION = [ |  | ||||||
|     "id_" => "integer primary key auto_increment", |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   protected function tableExists(string $tableName): bool { |   protected function tableExists(string $tableName): bool { | ||||||
|     $db = $this->db; |     $db = $this->db; | ||||||
|     $found = $db->get([ |     $found = $db->get([ | ||||||
|  | |||||||
| @ -9,6 +9,10 @@ class PgsqlStorage extends CapacitorStorage { | |||||||
|   const SERDATA_DEFINITION = "text"; |   const SERDATA_DEFINITION = "text"; | ||||||
|   const SERSUM_DEFINITION = "varchar(40)"; |   const SERSUM_DEFINITION = "varchar(40)"; | ||||||
|   const SERTS_DEFINITION = "timestamp"; |   const SERTS_DEFINITION = "timestamp"; | ||||||
|  |   const GENSERIAL_DEFINITION = "serial primary key"; | ||||||
|  |   const GENTEXT_DEFINITION = "text"; | ||||||
|  |   const GENBOOL_DEFINITION = "boolean"; | ||||||
|  |   const GENUUID_DEFINITION = "uuid"; | ||||||
| 
 | 
 | ||||||
|   function __construct($pgsql) { |   function __construct($pgsql) { | ||||||
|     $this->db = Pgsql::with($pgsql); |     $this->db = Pgsql::with($pgsql); | ||||||
| @ -20,10 +24,6 @@ class PgsqlStorage extends CapacitorStorage { | |||||||
|     return $this->db; |     return $this->db; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const PRIMARY_KEY_DEFINITION = [ |  | ||||||
|     "id_" => "serial primary key", |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   protected function tableExists(string $tableName): bool { |   protected function tableExists(string $tableName): bool { | ||||||
|     if (($index = strpos($tableName, ".")) !== false) { |     if (($index = strpos($tableName, ".")) !== false) { | ||||||
|       $schemaName = substr($tableName, 0, $index); |       $schemaName = substr($tableName, 0, $index); | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ use nulib\db\CapacitorStorage; | |||||||
|  * Class SqliteStorage |  * Class SqliteStorage | ||||||
|  */ |  */ | ||||||
| class SqliteStorage extends CapacitorStorage { | class SqliteStorage extends CapacitorStorage { | ||||||
|  |   const GENSERIAL_DEFINITION = "integer primary key autoincrement"; | ||||||
|  | 
 | ||||||
|   function __construct($sqlite) { |   function __construct($sqlite) { | ||||||
|     $this->db = Sqlite::with($sqlite); |     $this->db = Sqlite::with($sqlite); | ||||||
|   } |   } | ||||||
| @ -19,10 +21,6 @@ class SqliteStorage extends CapacitorStorage { | |||||||
|     return $this->db; |     return $this->db; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const PRIMARY_KEY_DEFINITION = [ |  | ||||||
|     "id_" => "integer primary key autoincrement", |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   protected function tableExists(string $tableName): bool { |   protected function tableExists(string $tableName): bool { | ||||||
|     $found = $this->db->get([ |     $found = $this->db->get([ | ||||||
|       # depuis la version 3.33.0 le nom officiel de la table est sqlite_schema,
 |       # depuis la version 3.33.0 le nom officiel de la table est sqlite_schema,
 | ||||||
| @ -40,22 +38,6 @@ class SqliteStorage extends CapacitorStorage { | |||||||
|     return new _sqliteMigration($migrations, $channel->getName()); |     return new _sqliteMigration($migrations, $channel->getName()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function channelExists(string $name, ?array &$row=null): bool { |  | ||||||
|     $row = $this->db->one([ |  | ||||||
|       "select", |  | ||||||
|       "from" => static::CHANNELS_TABLE, |  | ||||||
|       "where" => ["name" => $name], |  | ||||||
|     ]); |  | ||||||
|     return $row !== null; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function getChannels(): iterable { |  | ||||||
|     return $this->db->all([ |  | ||||||
|       "select", |  | ||||||
|       "from" => static::CHANNELS_TABLE, |  | ||||||
|     ]); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   protected function _addToChannelsSql(CapacitorChannel $channel): array { |   protected function _addToChannelsSql(CapacitorChannel $channel): array { | ||||||
|     $sql = parent::_addToChannelsSql($channel); |     $sql = parent::_addToChannelsSql($channel); | ||||||
|     $sql[0] = "insert or ignore"; |     $sql[0] = "insert or ignore"; | ||||||
|  | |||||||
| @ -117,4 +117,18 @@ class file { | |||||||
|     } |     } | ||||||
|     return $file; |     return $file; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   static function string_reader(string $content, ?callable $func=null): MemoryStream { | ||||||
|  |     $file = new MemoryStream(); | ||||||
|  |     $file->fwrite($content); | ||||||
|  |     $file->rewind(); | ||||||
|  |     if ($func !== null) { | ||||||
|  |       try { | ||||||
|  |         $func($file); | ||||||
|  |       } finally { | ||||||
|  |         $file ->close(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return $file; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -52,6 +52,7 @@ abstract class AbstractReader implements IReader { | |||||||
|     $this->headers = $params["headers"] ?? static::HEADERS; |     $this->headers = $params["headers"] ?? static::HEADERS; | ||||||
|     $this->useHeaders = $params["use_headers"] ?? static::USE_HEADERS; |     $this->useHeaders = $params["use_headers"] ?? static::USE_HEADERS; | ||||||
|     $this->input = $params["input"] ?? static::INPUT; |     $this->input = $params["input"] ?? static::INPUT; | ||||||
|  |     $this->skip = $params["skip"] ?? 0; | ||||||
|     $this->trim = boolval($params["trim"] ?? static::TRIM); |     $this->trim = boolval($params["trim"] ?? static::TRIM); | ||||||
|     $this->emptyAsNull = boolval($params["empty_as_null"] ?? static::EMPTY_AS_NULL); |     $this->emptyAsNull = boolval($params["empty_as_null"] ?? static::EMPTY_AS_NULL); | ||||||
|     $this->parseNone = boolval($params["parse_none"] ?? static::PARSE_NONE); |     $this->parseNone = boolval($params["parse_none"] ?? static::PARSE_NONE); | ||||||
| @ -67,6 +68,8 @@ abstract class AbstractReader implements IReader { | |||||||
| 
 | 
 | ||||||
|   protected $input; |   protected $input; | ||||||
| 
 | 
 | ||||||
|  |   protected int $skip; | ||||||
|  | 
 | ||||||
|   protected bool $trim; |   protected bool $trim; | ||||||
| 
 | 
 | ||||||
|   protected bool $emptyAsNull; |   protected bool $emptyAsNull; | ||||||
| @ -81,15 +84,16 @@ abstract class AbstractReader implements IReader { | |||||||
| 
 | 
 | ||||||
|   protected function cookRow(array &$row): bool { |   protected function cookRow(array &$row): bool { | ||||||
|     if (!$this->useHeaders) return true; |     if (!$this->useHeaders) return true; | ||||||
|     if ($this->isrc == 0) { |     if ($this->skip > 0) { | ||||||
|  |       $this->skip--; | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     if ($this->headers === null) { | ||||||
|       # ligne d'en-tête
 |       # ligne d'en-tête
 | ||||||
|       $headers = $this->headers; |  | ||||||
|       if ($headers === null) { |  | ||||||
|       if ($this->schema === null) $headers = null; |       if ($this->schema === null) $headers = null; | ||||||
|       else $headers = array_keys($this->schema); |       else $headers = array_keys($this->schema); | ||||||
|       if ($headers === null) $headers = $row; |       if ($headers === null) $headers = $row; | ||||||
|       $this->headers = $headers; |       $this->headers = $headers; | ||||||
|       } |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     A::ensure_size($row, count($this->headers)); |     A::ensure_size($row, count($this->headers)); | ||||||
|  | |||||||
| @ -1,124 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace nulib\tools; |  | ||||||
| 
 |  | ||||||
| use nulib\app; |  | ||||||
| use nulib\app\cli\Application; |  | ||||||
| use nulib\app\RunFile; |  | ||||||
| use nulib\ExitError; |  | ||||||
| use nulib\ext\yaml; |  | ||||||
| use nulib\os\path; |  | ||||||
| use nulib\os\proc\Cmd; |  | ||||||
| use nulib\os\sh; |  | ||||||
| use nulib\output\msg; |  | ||||||
| 
 |  | ||||||
| class BgLauncherApp extends Application { |  | ||||||
|   const ACTION_INFOS = 0, ACTION_START = 1, ACTION_STOP = 2; |  | ||||||
|   const ARGS = [ |  | ||||||
|     "purpose" => "lancer un script en tâche de fond", |  | ||||||
|     "usage" => "ApplicationClass args...", |  | ||||||
| 
 |  | ||||||
|     "sections" => [ |  | ||||||
|       parent::VERBOSITY_SECTION, |  | ||||||
|     ], |  | ||||||
| 
 |  | ||||||
|     ["-i", "--infos", "name" => "action", "value" => self::ACTION_INFOS, |  | ||||||
|       "help" => "Afficher des informations sur la tâche", |  | ||||||
|     ], |  | ||||||
|     ["-s", "--start", "name" => "action", "value" => self::ACTION_START, |  | ||||||
|       "help" => "Démarrer la tâche", |  | ||||||
|     ], |  | ||||||
|     ["-k", "--stop", "name" => "action", "value" => self::ACTION_STOP, |  | ||||||
|       "help" => "Arrêter la tâche", |  | ||||||
|     ], |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   protected int $action = self::ACTION_START; |  | ||||||
| 
 |  | ||||||
|   protected ?array $args = null; |  | ||||||
| 
 |  | ||||||
|   static function show_infos(RunFile $runfile, ?int $level=null): void { |  | ||||||
|     msg::print($runfile->getDesc(), $level); |  | ||||||
|     msg::print(yaml::with(["data" => $runfile->read()]), ($level ?? 0) - 1); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function main() { |  | ||||||
|     $args = $this->args; |  | ||||||
| 
 |  | ||||||
|     $appClass = $args[0] ?? null; |  | ||||||
|     if ($appClass === null) { |  | ||||||
|       self::die("Vous devez spécifier la classe de l'application"); |  | ||||||
|     } |  | ||||||
|     $appClass = $args[0] = str_replace("/", "\\", $appClass); |  | ||||||
|     if (!class_exists($appClass)) { |  | ||||||
|       self::die("$appClass: classe non trouvée"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     $useRunfile = constant("$appClass::USE_RUNFILE"); |  | ||||||
|     if (!$useRunfile) { |  | ||||||
|       self::die("Cette application ne supporte le lancement en tâche de fond"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     $runfile = app::with($appClass)->getRunfile(); |  | ||||||
|     switch ($this->action) { |  | ||||||
|     case self::ACTION_START: |  | ||||||
|       $argc = count($args); |  | ||||||
|       $appClass::_manage_runfile($argc, $args, $runfile); |  | ||||||
|       if ($runfile->warnIfLocked()) self::exit(app::EC_LOCKED); |  | ||||||
|       array_splice($args, 0, 0, [ |  | ||||||
|         PHP_BINARY, |  | ||||||
|         path::abspath(NULIB_APP_app_launcher), |  | ||||||
|       ]); |  | ||||||
|       app::params_putenv(); |  | ||||||
|       self::_start($args, $runfile); |  | ||||||
|       break; |  | ||||||
|     case self::ACTION_STOP: |  | ||||||
|       self::_stop($runfile); |  | ||||||
|       self::show_infos($runfile, -1); |  | ||||||
|       break; |  | ||||||
|     case self::ACTION_INFOS: |  | ||||||
|       self::show_infos($runfile); |  | ||||||
|       break; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public static function _start(array $args, Runfile $runfile): void { |  | ||||||
|     $pid = pcntl_fork(); |  | ||||||
|     if ($pid == -1) { |  | ||||||
|       # parent, impossible de forker
 |  | ||||||
|       throw new ExitError(app::EC_FORK_PARENT, "Unable to fork"); |  | ||||||
|     } elseif (!$pid) { |  | ||||||
|       # child, fork ok
 |  | ||||||
|       $runfile->wfPrepare($pid); |  | ||||||
|       $outfile = $runfile->getOutfile() ?? "/tmp/NULIB_APP_app_console.out"; |  | ||||||
|       $exitcode = app::EC_FORK_CHILD; |  | ||||||
|       try { |  | ||||||
|         # rediriger STDIN, STDOUT et STDERR
 |  | ||||||
|         fclose(fopen($outfile, "wb")); // vider le fichier
 |  | ||||||
|         fclose(STDIN); $in = fopen("/dev/null", "rb"); |  | ||||||
|         fclose(STDOUT); $out = fopen($outfile, "ab"); |  | ||||||
|         fclose(STDERR); $err = fopen($outfile, "ab"); |  | ||||||
|         # puis lancer la commande
 |  | ||||||
|         $cmd = new Cmd($args); |  | ||||||
|         $cmd->addSource("/g/init.env"); |  | ||||||
|         $cmd->addRedir("both", $outfile, true); |  | ||||||
|         $cmd->fork_exec($exitcode, false); |  | ||||||
|         sh::_waitpid(-$pid, $exitcode); |  | ||||||
|       } finally { |  | ||||||
|         $runfile->wfReaped($exitcode); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public static function _stop(Runfile $runfile): bool { |  | ||||||
|     $data = $runfile->read(); |  | ||||||
|     $pid = $runfile->_getCid($data); |  | ||||||
|     msg::action("stop $pid"); |  | ||||||
|     if ($runfile->wfKill($reason)) { |  | ||||||
|       msg::asuccess(); |  | ||||||
|       return true; |  | ||||||
|     } else { |  | ||||||
|       msg::afailure($reason); |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,53 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace nulib\tools; |  | ||||||
| 
 |  | ||||||
| use nulib\app; |  | ||||||
| use nulib\app\cli\Application; |  | ||||||
| use nulib\output\msg; |  | ||||||
| use nulib\php\time\DateTime; |  | ||||||
| use nulib\text\words; |  | ||||||
| 
 |  | ||||||
| class SteamTrainApp extends Application { |  | ||||||
|   const PROJDIR = __DIR__.'/../..'; |  | ||||||
|   const TITLE = "Train à vapeur"; |  | ||||||
|   const USE_LOGFILE = true; |  | ||||||
|   const USE_RUNFILE = true; |  | ||||||
|   const USE_RUNLOCK = true; |  | ||||||
| 
 |  | ||||||
|   const ARGS = [ |  | ||||||
|     "purpose" => self::TITLE, |  | ||||||
|     "description" => <<<EOT |  | ||||||
| Cette application peut être utilisée pour tester le lancement des tâches de fond |  | ||||||
| EOT, |  | ||||||
| 
 |  | ||||||
|     ["-c", "--count", "args" => 1, |  | ||||||
|       "help" => "spécifier le nombre d'étapes", |  | ||||||
|     ], |  | ||||||
|     ["-f", "--force-enabled", "value" => true, |  | ||||||
|       "help" => "lancer la commande même si les tâches planifiées sont désactivées", |  | ||||||
|     ], |  | ||||||
|     ["-n", "--no-install-signal-handler", "value" => false, |  | ||||||
|       "help" => "ne pas installer le gestionnaire de signaux", |  | ||||||
|     ], |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   protected $count = 100; |  | ||||||
| 
 |  | ||||||
|   protected bool $forceEnabled = false; |  | ||||||
| 
 |  | ||||||
|   protected bool $installSignalHandler = true; |  | ||||||
| 
 |  | ||||||
|   function main() { |  | ||||||
|     app::check_bgapplication_enabled($this->forceEnabled); |  | ||||||
|     if ($this->installSignalHandler) app::install_signal_handler(); |  | ||||||
|     $count = intval($this->count); |  | ||||||
|     msg::info("Starting train for ".words::q($count, "step#s")); |  | ||||||
|     app::action("Running train...", $count); |  | ||||||
|     for ($i = 1; $i <= $count; $i++) { |  | ||||||
|       msg::print("Tchou-tchou! x $i"); |  | ||||||
|       app::step(); |  | ||||||
|       sleep(1); |  | ||||||
|     } |  | ||||||
|     msg::info("Stopping train at ".new DateTime()); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,39 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 |  | ||||||
| source "$(dirname -- "$0")/../../load.sh" || exit 1 |  | ||||||
| 
 |  | ||||||
| declare -A DESTDIRS=( |  | ||||||
|     [template-_bg_launcher.php]=sbin |  | ||||||
|     [template-.launcher.php]=_cli |  | ||||||
|     [template-_wrapper.sh]=_cli |  | ||||||
| ) |  | ||||||
| declare -A MODES=( |  | ||||||
|     [template-_bg_launcher.php]=+x |  | ||||||
|     [template-.launcher.php]= |  | ||||||
|     [template-_wrapper.sh]=+x |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| projdir= |  | ||||||
| args=( |  | ||||||
|     "copier les templates dans le projet en cours" |  | ||||||
|     #"usage" |  | ||||||
|     -d:,--projdir: . |  | ||||||
| ) |  | ||||||
| parse_args "$@"; set -- "${args[@]}" |  | ||||||
| 
 |  | ||||||
| if [ -n "$projdir" ]; then |  | ||||||
|     cd "$projdir" || die |  | ||||||
| fi |  | ||||||
| 
 |  | ||||||
| [ -f composer.json ] || die "$(basename "$(dirname "$(pwd)")"): n'est pas un projet composer" |  | ||||||
| 
 |  | ||||||
| setx -a templates=ls_files "$MYDIR" "template-*" |  | ||||||
| for template in "${templates[@]}"; do |  | ||||||
|     destdir="${DESTDIRS[$template]}" |  | ||||||
|     [ -n "$destdir" ] || die "$template: la destination n'est pas configurée" |  | ||||||
|     mode="${MODES[$template]}" |  | ||||||
|     destname="${template#template-}" |  | ||||||
| 
 |  | ||||||
|     tail -n+4 "$MYDIR/$template" >"$destdir/$destname" |  | ||||||
|     [ -n "$mode" ] && chmod "$mode" "$destdir/$destname" |  | ||||||
| done |  | ||||||
| @ -7,6 +7,12 @@ use nulib\db\Capacitor; | |||||||
| use nulib\db\CapacitorChannel; | use nulib\db\CapacitorChannel; | ||||||
| 
 | 
 | ||||||
| class SqliteStorageTest extends TestCase { | class SqliteStorageTest extends TestCase { | ||||||
|  |   static function Txx(...$values): void { | ||||||
|  |     foreach ($values as $value) { | ||||||
|  |       var_export($value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   function _testChargeStrings(SqliteStorage $storage, ?string $channel) { |   function _testChargeStrings(SqliteStorage $storage, ?string $channel) { | ||||||
|     $storage->reset($channel); |     $storage->reset($channel); | ||||||
|     $storage->charge($channel, "first"); |     $storage->charge($channel, "first"); | ||||||
| @ -67,7 +73,8 @@ class SqliteStorageTest extends TestCase { | |||||||
|     $capacitor->charge(["name" => "third", "age" => 15]); |     $capacitor->charge(["name" => "third", "age" => 15]); | ||||||
|     $capacitor->charge(["name" => "fourth", "age" => 20]); |     $capacitor->charge(["name" => "fourth", "age" => 20]); | ||||||
| 
 | 
 | ||||||
|     $setDone = function ($item, $row, $suffix=null) { |     $setDone = function ($row, $suffix=null) { | ||||||
|  |       $item = $row["item"]; | ||||||
|       $updates = ["done" => 1]; |       $updates = ["done" => 1]; | ||||||
|       if ($suffix !== null) { |       if ($suffix !== null) { | ||||||
|         $item["name"] .= $suffix; |         $item["name"] .= $suffix; | ||||||
| @ -76,9 +83,9 @@ class SqliteStorageTest extends TestCase { | |||||||
|       return $updates; |       return $updates; | ||||||
|     }; |     }; | ||||||
|     $capacitor->each(["age" => [">", 10]], $setDone, ["++"]); |     $capacitor->each(["age" => [">", 10]], $setDone, ["++"]); | ||||||
|     $capacitor->each(["done" => 0], $setDone, null); |     $capacitor->each(["done" => 0], $setDone); | ||||||
| 
 | 
 | ||||||
|     Txx(cl::all($capacitor->discharge(false))); |     self::Txx(cl::all($capacitor->discharge(false))); | ||||||
|     $capacitor->close(); |     $capacitor->close(); | ||||||
|     self::assertTrue(true); |     self::assertTrue(true); | ||||||
|   } |   } | ||||||
| @ -133,16 +140,16 @@ class SqliteStorageTest extends TestCase { | |||||||
|     $capacitor->charge(["a" => null, "b" => null]); |     $capacitor->charge(["a" => null, "b" => null]); | ||||||
|     $capacitor->charge(["a" => "first", "b" => "second"]); |     $capacitor->charge(["a" => "first", "b" => "second"]); | ||||||
| 
 | 
 | ||||||
|     Txx("=== all"); |     self::Txx("=== all"); | ||||||
|     /** @var Sqlite $sqlite */ |     /** @var Sqlite $sqlite */ | ||||||
|     $sqlite = $capacitor->getStorage()->db(); |     $sqlite = $capacitor->getStorage()->db(); | ||||||
|     Txx(cl::all($sqlite->all([ |     self::Txx(cl::all($sqlite->all([ | ||||||
|       "select", |       "select", | ||||||
|       "from" => $capacitor->getChannel()->getTableName(), |       "from" => $capacitor->getChannel()->getTableName(), | ||||||
|     ]))); |     ]))); | ||||||
|     Txx("=== each"); |     self::Txx("=== each"); | ||||||
|     $capacitor->each(null, function ($item, $values) { |     $capacitor->each(null, function ($row) { | ||||||
|       Txx($values); |       self::Txx($row); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $capacitor->close(); |     $capacitor->close(); | ||||||
| @ -170,99 +177,100 @@ class SqliteStorageTest extends TestCase { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $capacitor->reset(); |     $capacitor->reset(); | ||||||
|     $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $values, ?array $pvalues) { |     $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame("first", $item["name"]); |       self::assertSame("first", $item["name"]); | ||||||
|       self::assertSame(5, $item["age"]); |       self::assertSame(5, $item["age"]); | ||||||
|       self::assertnotnull($values); |       self::assertnotnull($row); | ||||||
|       self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($values)); |       self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 5, |         "age" => 5, | ||||||
|         "item" => $item, |         "item" => $item, | ||||||
|       ], cl::select($values, ["name", "age", "item"])); |       ], cl::select($row, ["name", "age", "item"])); | ||||||
|       self::assertNull($pvalues); |       self::assertNull($prow); | ||||||
|     }); |     }); | ||||||
|     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) { |     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame("first", $item["name"]); |       self::assertSame("first", $item["name"]); | ||||||
|       self::assertSame(10, $item["age"]); |       self::assertSame(10, $item["age"]); | ||||||
|       self::assertnotnull($values); |       self::assertnotnull($row); | ||||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); |       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 10, |         "age" => 10, | ||||||
|         "done" => 0, |         "done" => 0, | ||||||
|         "notes" => null, |         "notes" => null, | ||||||
|         "item" => $item, |         "item" => $item, | ||||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); |       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||||
|       self::assertNotNull($pvalues); |       self::assertNotNull($prow); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 5, |         "age" => 5, | ||||||
|         "done" => 0, |         "done" => 0, | ||||||
|         "notes" => null, |         "notes" => null, | ||||||
|         "item" => ["name" => "first", "age" => 5], |         "item" => ["name" => "first", "age" => 5], | ||||||
|       ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); |       ], cl::select($prow, ["name", "age", "done", "notes", "item"])); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $capacitor->each(null, function($item, ?array $values) { |     $capacitor->each(null, function(array $row) { | ||||||
|  |       $item = $row["item"]; | ||||||
|       self::assertSame("first", $item["name"]); |       self::assertSame("first", $item["name"]); | ||||||
|       self::assertSame(10, $item["age"]); |       self::assertSame(10, $item["age"]); | ||||||
|       self::assertnotnull($values); |       self::assertnotnull($row); | ||||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); |       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 10, |         "age" => 10, | ||||||
|         "done" => 0, |         "done" => 0, | ||||||
|         "notes" => null, |         "notes" => null, | ||||||
|         "item" => $item, |         "item" => $item, | ||||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); |       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||||
|       return [ |       return [ | ||||||
|         "done" => 1, |         "done" => 1, | ||||||
|         "notes" => "modified", |         "notes" => "modified", | ||||||
|       ]; |       ]; | ||||||
|     }); |     }); | ||||||
|     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) { |     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame("first", $item["name"]); |       self::assertSame("first", $item["name"]); | ||||||
|       self::assertSame(10, $item["age"]); |       self::assertSame(10, $item["age"]); | ||||||
|       self::assertnotnull($values); |       self::assertnotnull($row); | ||||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); |       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 10, |         "age" => 10, | ||||||
|         "done" => 1, |         "done" => 1, | ||||||
|         "notes" => "modified", |         "notes" => "modified", | ||||||
|         "item" => $item, |         "item" => $item, | ||||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); |       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||||
|       self::assertNotNull($pvalues); |       self::assertNotNull($prow); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 10, |         "age" => 10, | ||||||
|         "done" => 1, |         "done" => 1, | ||||||
|         "notes" => "modified", |         "notes" => "modified", | ||||||
|         "item" => $item, |         "item" => $item, | ||||||
|       ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); |       ], cl::select($prow, ["name", "age", "done", "notes", "item"])); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $values, ?array $pvalues) { |     $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame("first", $item["name"]); |       self::assertSame("first", $item["name"]); | ||||||
|       self::assertSame(20, $item["age"]); |       self::assertSame(20, $item["age"]); | ||||||
|       self::assertnotnull($values); |       self::assertnotnull($row); | ||||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); |       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 20, |         "age" => 20, | ||||||
|         "done" => 1, |         "done" => 1, | ||||||
|         "notes" => "modified", |         "notes" => "modified", | ||||||
|         "item" => $item, |         "item" => $item, | ||||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); |       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||||
|       self::assertNotNull($pvalues); |       self::assertNotNull($prow); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", |         "name" => "first", | ||||||
|         "age" => 10, |         "age" => 10, | ||||||
|         "done" => 1, |         "done" => 1, | ||||||
|         "notes" => "modified", |         "notes" => "modified", | ||||||
|         "item" => ["name" => "first", "age" => 10], |         "item" => ["name" => "first", "age" => 10], | ||||||
|       ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); |       ], cl::select($prow, ["name", "age", "done", "notes", "item"])); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -287,55 +295,55 @@ class SqliteStorageTest extends TestCase { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $capacitor->reset(); |     $capacitor->reset(); | ||||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $values, ?array $pvalues) { |     $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", "age" => 5, |         "name" => "first", "age" => 5, | ||||||
|         "item" => $item, |         "item" => $item, | ||||||
|       ], cl::select($values, ["name", "age", "item"])); |       ], cl::select($row, ["name", "age", "item"])); | ||||||
|       return ["item" => null]; |       return ["item" => null]; | ||||||
|     }); |     }); | ||||||
|     self::assertSame(1, $nbModified); |     self::assertSame(1, $nbModified); | ||||||
|     sleep(1); |     sleep(1); | ||||||
|     # nb: on met des sleep() pour que la date de modification soit systématiquement différente
 |     # nb: on met des sleep() pour que la date de modification soit systématiquement différente
 | ||||||
| 
 | 
 | ||||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) { |     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", "age" => 10, |         "name" => "first", "age" => 10, | ||||||
|         "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", |         "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", | ||||||
|       ], cl::select($values, ["name", "age", "item", "item__sum_"])); |       ], cl::select($row, ["name", "age", "item", "item__sum_"])); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", "age" => 5, |         "name" => "first", "age" => 5, | ||||||
|         "item" => null, "item__sum_" => null, |         "item" => null, "item__sum_" => null, | ||||||
|       ], cl::select($pvalues, ["name", "age", "item", "item__sum_"])); |       ], cl::select($prow, ["name", "age", "item", "item__sum_"])); | ||||||
|       return ["item" => null]; |       return ["item" => null]; | ||||||
|     }); |     }); | ||||||
|     self::assertSame(1, $nbModified); |     self::assertSame(1, $nbModified); | ||||||
|     sleep(1); |     sleep(1); | ||||||
| 
 | 
 | ||||||
|     # pas de modification ici
 |     # pas de modification ici
 | ||||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) { |     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", "age" => 10, |         "name" => "first", "age" => 10, | ||||||
|         "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", |         "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", | ||||||
|       ], cl::select($values, ["name", "age", "item", "item__sum_"])); |       ], cl::select($row, ["name", "age", "item", "item__sum_"])); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", "age" => 10, |         "name" => "first", "age" => 10, | ||||||
|         "item" => null, "item__sum_" => null, |         "item" => null, "item__sum_" => null, | ||||||
|       ], cl::select($pvalues, ["name", "age", "item", "item__sum_"])); |       ], cl::select($prow, ["name", "age", "item", "item__sum_"])); | ||||||
|       return ["item" => null]; |       return ["item" => null]; | ||||||
|     }); |     }); | ||||||
|     self::assertSame(0, $nbModified); |     self::assertSame(0, $nbModified); | ||||||
|     sleep(1); |     sleep(1); | ||||||
| 
 | 
 | ||||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $values, ?array $pvalues) { |     $nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $row, ?array $prow) { | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", "age" => 20, |         "name" => "first", "age" => 20, | ||||||
|         "item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5", |         "item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5", | ||||||
|       ], cl::select($values, ["name", "age", "item", "item__sum_"])); |       ], cl::select($row, ["name", "age", "item", "item__sum_"])); | ||||||
|       self::assertSame([ |       self::assertSame([ | ||||||
|         "name" => "first", "age" => 10, |         "name" => "first", "age" => 10, | ||||||
|         "item" => null, "item__sum_" => null, |         "item" => null, "item__sum_" => null, | ||||||
|       ], cl::select($pvalues, ["name", "age", "item", "item__sum_"])); |       ], cl::select($prow, ["name", "age", "item", "item__sum_"])); | ||||||
|       return ["item" => null]; |       return ["item" => null]; | ||||||
|     }); |     }); | ||||||
|     self::assertSame(1, $nbModified); |     self::assertSame(1, $nbModified); | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ namespace nulib\db\sqlite; | |||||||
| 
 | 
 | ||||||
| use Exception; | use Exception; | ||||||
| use nulib\tests\TestCase; | use nulib\tests\TestCase; | ||||||
|  | use nulib\ValueException; | ||||||
| 
 | 
 | ||||||
| class SqliteTest extends TestCase { | class SqliteTest extends TestCase { | ||||||
|   const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)"; |   const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)"; | ||||||
| @ -12,8 +13,8 @@ class SqliteTest extends TestCase { | |||||||
|   function testMigration() { |   function testMigration() { | ||||||
|     $sqlite = new Sqlite(":memory:", [ |     $sqlite = new Sqlite(":memory:", [ | ||||||
|       "migration" => [ |       "migration" => [ | ||||||
|         self::CREATE_PERSON, |         "create" => self::CREATE_PERSON, | ||||||
|         self::INSERT_JEPHTE, |         "insert" => self::INSERT_JEPHTE, | ||||||
|       ], |       ], | ||||||
|     ]); |     ]); | ||||||
|     self::assertSame("clain", $sqlite->get("select nom, age from person")); |     self::assertSame("clain", $sqlite->get("select nom, age from person")); | ||||||
| @ -30,15 +31,15 @@ class SqliteTest extends TestCase { | |||||||
|     ], $sqlite->get("select nom, age from person where nom = 'payet'", null, true)); |     ], $sqlite->get("select nom, age from person where nom = 'payet'", null, true)); | ||||||
| 
 | 
 | ||||||
|     self::assertSame([ |     self::assertSame([ | ||||||
|       ["key" => "0", "value" => self::CREATE_PERSON, "done" => 1], |       ["channel" => "", "name" => "create", "done" => 1], | ||||||
|       ["key" => "1", "value" => self::INSERT_JEPHTE, "done" => 1], |       ["channel" => "", "name" => "insert", "done" => 1], | ||||||
|     ], iterator_to_array($sqlite->all("select key, value, done from _migration"))); |     ], iterator_to_array($sqlite->all("select channel, name, done from _migration"))); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function testException() { |   function testException() { | ||||||
|     $sqlite = new Sqlite(":memory:"); |     $sqlite = new Sqlite(":memory:"); | ||||||
|     self::assertException(Exception::class, [$sqlite, "exec"], "prout"); |     self::assertException(Exception::class, [$sqlite, "exec"], "prout"); | ||||||
|     self::assertException(SqliteException::class, [$sqlite, "exec"], ["prout"]); |     self::assertException(ValueException::class, [$sqlite, "exec"], ["prout"]); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected function assertInserted(Sqlite $sqlite, array $row, array $query): void { |   protected function assertInserted(Sqlite $sqlite, array $row, array $query): void { | ||||||
| @ -141,6 +142,10 @@ class SqliteTest extends TestCase { | |||||||
| 
 | 
 | ||||||
|     self::assertSame([ |     self::assertSame([ | ||||||
|       ["count" => 2], |       ["count" => 2], | ||||||
|     ], iterator_to_array($sqlite->all(["select count(name) as count from user", "group by" => ["amount"], "having" => ["count(name) = 2"]]))); |     ], iterator_to_array($sqlite->all([ | ||||||
|  |       "select count(name) as count from user", | ||||||
|  |       "group by" => ["amount"], | ||||||
|  |       "having" => ["count(name) = 2"], | ||||||
|  |     ]))); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ function build_check_env() { | |||||||
|             if template_copy_missing "$PROJDIR/$file"; then |             if template_copy_missing "$PROJDIR/$file"; then | ||||||
|                 updated=1 |                 updated=1 | ||||||
|                 setx name=basename -- "$file" |                 setx name=basename -- "$file" | ||||||
|                 name="${name#.}"; name="${name%.}" |                 name="${name#.}"; name="${name%.*}" | ||||||
|                 setx file=dirname -- "$file" |                 setx file=dirname -- "$file" | ||||||
|                 file="$file/$name" |                 file="$file/$name" | ||||||
|                 updatedfiles+=("$file") |                 updatedfiles+=("$file") | ||||||
| @ -86,7 +86,7 @@ function build_check_env() { | |||||||
|                 if template_copy_missing "$file"; then |                 if template_copy_missing "$file"; then | ||||||
|                     updated=1 |                     updated=1 | ||||||
|                     setx name=basename -- "$file" |                     setx name=basename -- "$file" | ||||||
|                     name="${name#.}"; name="${name%.}" |                     name="${name#.}"; name="${name%.*}" | ||||||
|                     setx file=dirname -- "$file" |                     setx file=dirname -- "$file" | ||||||
|                     file="$file/$name" |                     file="$file/$name" | ||||||
|                     updatedfiles+=("${file#$PROJDIR/}") |                     updatedfiles+=("${file#$PROJDIR/}") | ||||||
|  | |||||||
| @ -1,12 +1,12 @@ | |||||||
| # TODO Faire une copie de ce script dans un répertoire de l'application web
 | # TODO Faire une copie de ce script dans un répertoire de l'application web
 | ||||||
| # (dans le répertoire _cli/ par défaut) et modifier les paramètres si nécessaire
 | # (dans le répertoire cli_config/ par défaut) et modifier les paramètres si nécessaire
 | ||||||
| #-------------------------------------------------------------------------------
 | #-------------------------------------------------------------------------------
 | ||||||
| <?php | <?php | ||||||
| require __DIR__ . '/../vendor/autoload.php'; | require __DIR__.'/../vendor/autoload.php'; | ||||||
| # Lancer une application en ligne de commande
 | # Lancer une application en ligne de commande
 | ||||||
| 
 | 
 | ||||||
| const NULIB_APP_app_params = [ | const NULIB_APP_app_params = [ | ||||||
|   "projdir" => __DIR__ . '/..', |   "projdir" => __DIR__.'/..', | ||||||
|   "appcode" => \app\config\bootstrap::APPCODE, |   "appcode" => \app\config\bootstrap::APPCODE, | ||||||
| ]; | ]; | ||||||
| require __DIR__.'/../vendor/nulib/base/php/src/app/cli/include-launcher.php'; | require __DIR__.'/../vendor/nulib/base/php/src/app/cli/include-launcher.php'; | ||||||
| @ -6,10 +6,10 @@ require __DIR__.'/../vendor/autoload.php'; | |||||||
| # Lancer une application en tâche de fond
 | # Lancer une application en tâche de fond
 | ||||||
| 
 | 
 | ||||||
| use nulib\app; | use nulib\app; | ||||||
| use nulib\tools\BgLauncherApp; | use nulib\cli\BgLauncherApp; | ||||||
| 
 | 
 | ||||||
| # chemin vers le lanceur PHP
 | # chemin vers le lanceur PHP
 | ||||||
| const NULIB_APP_app_launcher = __DIR__.'/../_cli/.launcher.php'; | const NULIB_APP_app_launcher = __DIR__.'/../@@CLI@@/.launcher.php'; | ||||||
| 
 | 
 | ||||||
| app::init([ | app::init([ | ||||||
|   "projdir" => __DIR__.'/..', |   "projdir" => __DIR__.'/..', | ||||||
| @ -1,5 +1,5 @@ | |||||||
| # TODO Faire une copie de ce script dans un répertoire de l'application web | # TODO Faire une copie de ce script dans un répertoire de l'application web | ||||||
| # (dans le répertoire _cli/ par défaut) et modifier les paramétres si nécessaire | # (dans le répertoire cli_config/ par défaut) et modifier les paramétres si nécessaire | ||||||
| #------------------------------------------------------------------------------- | #------------------------------------------------------------------------------- | ||||||
| #!/bin/bash | #!/bin/bash | ||||||
| # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 | # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 | ||||||
| @ -57,7 +57,7 @@ Vérification des liens..." | |||||||
|         for i in *.php*; do |         for i in *.php*; do | ||||||
|             [ -f "$i" ] || continue |             [ -f "$i" ] || continue | ||||||
|             name="bin/${i%.*}.php" |             name="bin/${i%.*}.php" | ||||||
|             dest="../_cli/_wrapper.sh" |             dest="../@@CLI@@/_wrapper.sh" | ||||||
|             link="../bin/${i%.*}.php" |             link="../bin/${i%.*}.php" | ||||||
|             if [ -L "$link" ]; then |             if [ -L "$link" ]; then | ||||||
|                 echo "* $name OK" |                 echo "* $name OK" | ||||||
| @ -40,11 +40,31 @@ p == 1 { | |||||||
|     ac_clean "$conf0" |     ac_clean "$conf0" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | declare -A PHPWRAPPER_DESTDIRS=( | ||||||
|  |     [_bg_launcher.php]=@@SBIN@@ | ||||||
|  |     [.launcher.php]=@@CLI@@ | ||||||
|  |     [_wrapper.sh]=@@CLI@@ | ||||||
|  | ) | ||||||
|  | declare -A PHPWRAPPER_MODES=( | ||||||
|  |     [_bg_launcher.php]=+x | ||||||
|  |     [.launcher.php]= | ||||||
|  |     [_wrapper.sh]=+x | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| projdir= | projdir= | ||||||
|  | install_phpwrappers=auto | ||||||
| args=( | args=( | ||||||
|     "Mettre à jour le script runphp" |     "Mettre à jour le script runphp" | ||||||
|     "[path/to/runphp]" |     "[path/to/runphp]" | ||||||
|     -d:,--projdir:PROJDIR . "Copier les fichiers pour un projet de l'université de la Réunion" |     -d:,--projdir:PROJDIR . "\ | ||||||
|  | Copier les fichiers pour un projet de l'université de la Réunion: | ||||||
|  | - BUILDENV0 et BUILDENV sont fixés à ..env.dist et .env | ||||||
|  | - les fichiers ..env.dist et .runphp.conf sont créés le cas échéant | ||||||
|  | - le script build est mis à jour | ||||||
|  | - les wrappers PHP pour la gestion des tâches de fond sont mis à jour le cas | ||||||
|  |   échéant" | ||||||
|  |     -p,--phpwrappers-install install_phpwrappers=1 "forcer l'installation des wrappers PHP" | ||||||
|  |     --np,--no-phpwrappers-install install_phpwrappers= "ne pas installer les wrappers PHP" | ||||||
| ) | ) | ||||||
| parse_args "$@"; set -- "${args[@]}" | parse_args "$@"; set -- "${args[@]}" | ||||||
| 
 | 
 | ||||||
| @ -93,6 +113,7 @@ else | |||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # (Re)construire le fichier destination | # (Re)construire le fichier destination | ||||||
|  | estep "$(relpath "$runphp")" | ||||||
| ( | ( | ||||||
|     cat "$preamble" |     cat "$preamble" | ||||||
|     echo |     echo | ||||||
| @ -102,24 +123,81 @@ fi | |||||||
| ) >"$runphp" | ) >"$runphp" | ||||||
| [ -x "$runphp" ] || chmod +x "$runphp" | [ -x "$runphp" ] || chmod +x "$runphp" | ||||||
| 
 | 
 | ||||||
|  | eval "$( | ||||||
|  | vars=(PROJDIR COMPOSERDIR COMPOSERPHAR VENDORDIR BUILDENV0 BUILDENV BUILD_FLAVOUR DIST IMAGENAME) | ||||||
|  | arrays=(BUILD_IMAGES DISTFILES TEMPLATEFILES VARFILES) | ||||||
|  | for var in "${vars[@]}"; do eval "$var="; done | ||||||
|  | for array in "${arrays[@]}"; do eval "$array=()"; done | ||||||
|  | source "$runphp" | ||||||
|  | for var in "${vars[@]}"; do echo_setv2 "$var"; done | ||||||
|  | for array in "${arrays[@]}"; do echo_seta2 "$array"; done | ||||||
|  | )" | ||||||
|  | 
 | ||||||
|  | estep "$(relpath "$rundir/Dockerfile.runphp")" | ||||||
| rsync -lpt "$MYDIR/Dockerfile.runphp" "$rundir/" | rsync -lpt "$MYDIR/Dockerfile.runphp" "$rundir/" | ||||||
| 
 | 
 | ||||||
| if [ -n "$projdir" ]; then | if [ -n "$projdir" ]; then | ||||||
|     if testdiff "$rundir/build" "$MYDIR/build"; then |     if testdiff "$rundir/build" "$MYDIR/build"; then | ||||||
|  |         estep "$(relpath "$rundir/build")" | ||||||
|         cp "$MYDIR/build" "$rundir/build" |         cp "$MYDIR/build" "$rundir/build" | ||||||
|         chmod +x "$rundir/build" |         chmod +x "$rundir/build" | ||||||
|     fi |     fi | ||||||
|     if [ ! -f "$projdir/..env.dist" ]; then |     if [ ! -f "$projdir/..env.dist" ]; then | ||||||
|  |         estep "$(relpath "$projdir/..env.dist")" | ||||||
|         sed <"$MYDIR/dot-build.env.dist" >"$projdir/..env.dist" ' |         sed <"$MYDIR/dot-build.env.dist" >"$projdir/..env.dist" ' | ||||||
| /^IMAGENAME=/s/=.*\//='"$(basename -- "$projdir")"'\// | /^IMAGENAME=/s/=.*\//='"$(basename -- "$projdir")"'\// | ||||||
| ' | ' | ||||||
|         initial_config=1 |         initial_config=1 | ||||||
|     fi |     fi | ||||||
|     if [ ! -f "$projdir/.runphp.conf" ]; then |     if [ ! -f "$projdir/.runphp.conf" ]; then | ||||||
|  |         estep "$(relpath "$projdir/.runphp.conf")" | ||||||
|         sed <"$MYDIR/dot-runphp.conf" >"$projdir/.runphp.conf" ' |         sed <"$MYDIR/dot-runphp.conf" >"$projdir/.runphp.conf" ' | ||||||
| /^RUNPHP=/s/=.*/=sbin\/runphp/ | /^RUNPHP=/s/=.*/=sbin\/runphp/ | ||||||
| ' | ' | ||||||
|     fi |     fi | ||||||
|  | 
 | ||||||
|  |     sbin_path=sbin | ||||||
|  |     cli_path=cli_config | ||||||
|  |     if [ "$install_phpwrappers" == auto ]; then | ||||||
|  |         if [ ! -f "$PROJDIR/$COMPOSERDIR/composer.json" ]; then | ||||||
|  |             # ce doit être un projet PHP | ||||||
|  |             install_phpwrappers= | ||||||
|  |         elif [ -d "$projdir/cli_config" ]; then | ||||||
|  |             install_phpwrappers=1 | ||||||
|  |             sbin_path=sbin | ||||||
|  |             cli_path=cli_config | ||||||
|  |         elif [ -d "$projdir/_cli" ]; then | ||||||
|  |             install_phpwrappers=1 | ||||||
|  |             sbin_path=sbin | ||||||
|  |             cli_path=_cli | ||||||
|  |         else | ||||||
|  |             install_phpwrappers= | ||||||
|  |         fi | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     if [ -n "$install_phpwrappers" ]; then | ||||||
|  |         setx -a phpwrappers=ls_files "$MYDIR" "phpwrapper-*" | ||||||
|  |         for phpwrapper in "${phpwrappers[@]}"; do | ||||||
|  |             destname="${phpwrapper#phpwrapper-}" | ||||||
|  |             destdir="${PHPWRAPPER_DESTDIRS[$destname]}" | ||||||
|  |             [ -n "$destdir" ] || die "$phpwrapper: la destination n'est pas configurée" | ||||||
|  |             mode="${PHPWRAPPER_MODES[$destname]}" | ||||||
|  | 
 | ||||||
|  |             case "$destdir" in | ||||||
|  |             @@SBIN@@) destdir="$PROJDIR/$sbin_path";; | ||||||
|  |             @@CLI@@) destdir="$PROJDIR/$cli_path";; | ||||||
|  |             *) destdir="$PROJDIR/$destdir";; | ||||||
|  |             esac | ||||||
|  | 
 | ||||||
|  |             estep "$(relpath "$destdir/$destname")" | ||||||
|  |             mkdir -p "$destdir" | ||||||
|  |             tail -n+4 "$MYDIR/$phpwrapper" | sed " | ||||||
|  | s|/@@SBIN@@/|/$sbin_path/| | ||||||
|  | s|/@@CLI@@/|/$cli_path/| | ||||||
|  | " >"$destdir/$destname" | ||||||
|  |             [ -n "$mode" ] && chmod "$mode" "$destdir/$destname" | ||||||
|  |         done | ||||||
|  |     fi | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| [ -n "$initial_config" ] | [ -n "$initial_config" ] | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								wip/pman.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								wip/pman.md
									
									
									
									
									
								
							| @ -6,9 +6,22 @@ outil pour gérer les projets PHP | |||||||
|   projets dépendants du projet courant |   projets dépendants du projet courant | ||||||
| * pver: gestion des versions. | * pver: gestion des versions. | ||||||
|   calculer la prochaine version en respectant semver |   calculer la prochaine version en respectant semver | ||||||
| * pmer: gérer les branches de features et hotfixes. | 
 | ||||||
| * prel: faire une release. | ## scripts de gestion de projet | ||||||
|   ces outils peuvent agir sur les projets dépendants: faire une release sur un | 
 | ||||||
|   projet downstream, ou synchroniser la version depuis un projet upstream | définir précisément le rôle des scripts | ||||||
|  | * pdist: créer la branche DIST, basculer dessus, merger MAIN dans DIST | ||||||
|  | * pmain: initialiser la	branche MAIN (si nouveau dépôt), basculer dessus, merger DEVELOP dans MAIN | ||||||
|  |   (s'occupe aussi de la configuration pman.conf) | ||||||
|  | * pdev: créer la branche DEVELOP, basculer dessus | ||||||
|  | * pwip: créer une branche WIP, basculer dessus si unique (ou laisser le choix), merger WIP dans DEVELOP | ||||||
|  | * PEUT-ETRE: pfix: créer une branche HOTFIX, basculer dessus si unique (ou laisser le choix), merger HOTFIX dans MAIN | ||||||
|  | * prel: faire une release de DEVELOP dans MAIN. à terme, support des branches de hotfix | ||||||
|  |   * il s'agit d'une spécialisation de pmain et/ou pfix pour la gestion des releases | ||||||
|  |   * à terme, gestion en cascade des projets dépendants: release sur un projet downstream, ou synchroniser la version depuis un projet upstream | ||||||
|  | 
 | ||||||
|  | il faudra supprimer | ||||||
|  | * pman: fonctionnalités réparties dans les autres scripts spécialisés | ||||||
|  | * pmer: fonctionnalités réperties dans les autres scripts spécialisés | ||||||
| 
 | 
 | ||||||
| -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary | -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user