From bbea373c19a55a185fbb84d0caad42b6e32f060e Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 15 Jan 2024 17:12:36 +0000 Subject: [PATCH] [core] use ArrayAccess rather than dynamic props for extra columns on the images table --- core/basethemelet.php | 2 +- core/imageboard/image.php | 44 ++++++++++++++--- ext/approval/main.php | 2 +- ext/approval/theme.php | 2 +- ext/comment/main.php | 4 +- ext/favorites/main.php | 2 +- ext/featured/main.php | 2 +- ext/numeric_score/main.php | 2 +- ext/numeric_score/theme.php | 5 +- ext/ouroboros_api/main.php | 6 +-- ext/post_titles/main.php | 5 +- ext/private_image/main.php | 2 +- ext/private_image/theme.php | 2 +- ext/rating/main.php | 14 +++--- ext/relationships/main.php | 6 +-- ext/relationships/test.php | 88 ++++++++++++++++----------------- ext/relationships/theme.php | 10 ++-- ext/trash/main.php | 6 +-- tests/phpstan.neon | 2 - themes/danbooru/view.theme.php | 6 +-- themes/danbooru2/view.theme.php | 6 +-- themes/lite/view.theme.php | 6 +-- 22 files changed, 125 insertions(+), 99 deletions(-) diff --git a/core/basethemelet.php b/core/basethemelet.php index b1ded94f..43733585 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -89,7 +89,7 @@ class BaseThemelet "data-post-id" => $id, ]; if(Extension::is_enabled(RatingsInfo::KEY)) { - $attrs["data-rating"] = $image->rating; + $attrs["data-rating"] = $image['rating']; } return A( diff --git a/core/imageboard/image.php b/core/imageboard/image.php index abf3f515..0d61f2fd 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -17,9 +17,8 @@ use GQLA\Query; * image per se, but could be a video, sound file, or any * other supported upload type. */ -#[\AllowDynamicProperties] #[Type(name: "Post")] -class Image +class Image implements \ArrayAccess { public const IMAGE_DIR = "images"; public const THUMBNAIL_DIR = "thumbs"; @@ -57,6 +56,8 @@ class Image public ?int $length = null; public ?string $tmp_file = null; + /** @var array */ + private array $dynamic_props = []; public static array $bool_props = ["locked", "lossless", "video", "audio", "image"]; public static array $int_props = ["id", "owner_id", "height", "width", "filesize", "length"]; @@ -75,20 +76,40 @@ class Image // some databases use table.name rather than name $name = str_replace("images.", "", $name); - // hax, this is likely the cause of much scrutinizer-ci complaints. if (is_null($value)) { - $this->$name = null; + $value = null; } elseif (in_array($name, self::$bool_props)) { - $this->$name = bool_escape((string)$value); + $value = bool_escape((string)$value); } elseif (in_array($name, self::$int_props)) { - $this->$name = int_escape((string)$value); - } else { + $value = int_escape((string)$value); + } + + if(property_exists($this, $name)) { $this->$name = $value; + } else { + $this->dynamic_props[$name] = $value; } } } } + public function offsetExists(mixed $offset): bool + { + return isset($this->dynamic_props[$offset]); + } + public function offsetGet(mixed $offset): mixed + { + return $this->dynamic_props[$offset]; + } + public function offsetSet(mixed $offset, mixed $value): void + { + $this->dynamic_props[$offset] = $value; + } + public function offsetUnset(mixed $offset): void + { + unset($this->dynamic_props[$offset]); + } + #[Field(name: "post_id")] public function graphql_oid(): int { @@ -262,6 +283,15 @@ class Image ); } + // For the future: automatically save dynamic props instead of + // requiring each extension to do it manually. + /* + $props_sql = "UPDATE images SET "; + $props_sql .= implode(", ", array_map(fn ($prop) => "$prop = :$prop", array_keys($this->dynamic_props))); + $props_sql .= " WHERE id = :id"; + $database->execute($props_sql, array_merge($this->dynamic_props, ["id" => $this->id])); + */ + $database->execute( "UPDATE images SET ". "lossless = :lossless, ". diff --git a/ext/approval/main.php b/ext/approval/main.php index 7bbbde8a..beb35087 100644 --- a/ext/approval/main.php +++ b/ext/approval/main.php @@ -208,7 +208,7 @@ class Approval extends Extension { global $user, $config; - if ($config->get_bool(ApprovalConfig::IMAGES) && $image->approved === false && !$user->can(Permissions::APPROVE_IMAGE) && $user->id !== $image->owner_id) { + if ($config->get_bool(ApprovalConfig::IMAGES) && $image['approved'] === false && !$user->can(Permissions::APPROVE_IMAGE) && $user->id !== $image->owner_id) { return false; } return true; diff --git a/ext/approval/theme.php b/ext/approval/theme.php index 61488868..2dc28226 100644 --- a/ext/approval/theme.php +++ b/ext/approval/theme.php @@ -14,7 +14,7 @@ class ApprovalTheme extends Themelet { public function get_image_admin_html(Image $image): HTMLElement { - if ($image->approved === true) { + if ($image['approved'] === true) { $form = SHM_SIMPLE_FORM( 'disapprove_image/'.$image->id, INPUT(["type" => 'hidden', "name" => 'image_id', "value" => $image->id]), diff --git a/ext/comment/main.php b/ext/comment/main.php index 528ce44a..284fe2a8 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -312,14 +312,14 @@ class CommentList extends Extension $image = Image::by_id((int)$row["image_id"]); if ( Extension::is_enabled(RatingsInfo::KEY) && !is_null($image) && - !in_array($image->rating, $user_ratings) + !in_array($image['rating'], $user_ratings) ) { $image = null; // this is "clever", I may live to regret it } if ( Extension::is_enabled(ApprovalInfo::KEY) && !is_null($image) && $config->get_bool(ApprovalConfig::IMAGES) && - $image->approved !== true + $image['approved'] !== true ) { $image = null; } diff --git a/ext/favorites/main.php b/ext/favorites/main.php index 81b427aa..c8b5ce19 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -107,7 +107,7 @@ class Favorites extends Extension public function onParseLinkTemplate(ParseLinkTemplateEvent $event): void { - $event->replace('$favorites', (string)$event->image->favorites); + $event->replace('$favorites', (string)$event->image['favorites']); } public function onUserBlockBuilding(UserBlockBuildingEvent $event): void diff --git a/ext/featured/main.php b/ext/featured/main.php index 277db059..86cbab67 100644 --- a/ext/featured/main.php +++ b/ext/featured/main.php @@ -65,7 +65,7 @@ class Featured extends Extension ); if (!is_null($image)) { if (Extension::is_enabled(RatingsInfo::KEY)) { - if (!in_array($image->rating, Ratings::get_user_class_privs($user))) { + if (!in_array($image['rating'], Ratings::get_user_class_privs($user))) { return; } } diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index 1824969a..351d6d2d 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -282,7 +282,7 @@ class NumericScore extends Extension public function onParseLinkTemplate(ParseLinkTemplateEvent $event): void { - $event->replace('$score', (string)$event->image->numeric_score); + $event->replace('$score', (string)$event->image['numeric_score']); } public function onHelpPageBuilding(HelpPageBuildingEvent $event): void diff --git a/ext/numeric_score/theme.php b/ext/numeric_score/theme.php index 6cbda2db..8381a686 100644 --- a/ext/numeric_score/theme.php +++ b/ext/numeric_score/theme.php @@ -10,10 +10,7 @@ class NumericScoreTheme extends Themelet { global $user, $page; $i_image_id = $image->id; - if (is_string($image->numeric_score)) { - $image->numeric_score = (int)$image->numeric_score; - } - $i_score = $image->numeric_score; + $i_score = (int)$image['numeric_score']; $html = " Current Score: $i_score diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index 571d9870..052d07cb 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -74,12 +74,12 @@ class _SafeOuroborosImage if (Extension::is_enabled(RatingsInfo::KEY) !== false) { // 'u' is not a "valid" rating - if ($img->rating == 's' || $img->rating == 'q' || $img->rating == 'e') { - $this->rating = $img->rating; + if ($img['rating'] == 's' || $img['rating'] == 'q' || $img['rating'] == 'e') { + $this->rating = $img['rating']; } } if (Extension::is_enabled(NumericScoreInfo::KEY) !== false) { - $this->score = $img->numeric_score; + $this->score = $img['numeric_score']; } $this->source = $img->source; diff --git a/ext/post_titles/main.php b/ext/post_titles/main.php index 074540c8..f2c8cf5f 100644 --- a/ext/post_titles/main.php +++ b/ext/post_titles/main.php @@ -77,8 +77,9 @@ class PostTitles extends Extension public function onBulkExport(BulkExportEvent $event): void { - $event->fields["title"] = $event->image->title; + $event->fields["title"] = $event->image['title']; } + public function onBulkImport(BulkImportEvent $event): void { if (array_key_exists("title", $event->fields) && $event->fields['title'] != null) { @@ -97,7 +98,7 @@ class PostTitles extends Extension { global $config; - $title = $image->title ?? ""; + $title = $image['title'] ?? ""; if (empty($title) && $config->get_bool(PostTitlesConfig::DEFAULT_TO_FILENAME)) { $info = pathinfo($image->filename); if (array_key_exists("extension", $info)) { diff --git a/ext/private_image/main.php b/ext/private_image/main.php index a4a8c25b..d7dfbe9d 100644 --- a/ext/private_image/main.php +++ b/ext/private_image/main.php @@ -117,7 +117,7 @@ class PrivateImage extends Extension { global $user, $page; - if ($event->image->private === true && $event->image->owner_id != $user->id && !$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) { + if ($event->image['private'] === true && $event->image->owner_id != $user->id && !$user->can(Permissions::SET_OTHERS_PRIVATE_IMAGES)) { $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link()); } diff --git a/ext/private_image/theme.php b/ext/private_image/theme.php index 65c12248..44387653 100644 --- a/ext/private_image/theme.php +++ b/ext/private_image/theme.php @@ -10,7 +10,7 @@ class PrivateImageTheme extends Themelet { public function get_image_admin_html(Image $image): string { - if ($image->private === false) { + if ($image['private'] === false) { $html = SHM_SIMPLE_FORM( 'privatize_image/'.$image->id, INPUT(["type" => 'hidden', "name" => 'image_id', "value" => $image->id]), diff --git a/ext/rating/main.php b/ext/rating/main.php index f01a1413..99c9ebe5 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -104,7 +104,7 @@ class Ratings extends Extension global $user; $user_view_level = Ratings::get_user_class_privs($user); - if (!in_array($image->rating, $user_view_level)) { + if (!in_array($image['rating'], $user_view_level)) { return false; } return true; @@ -185,7 +185,7 @@ class Ratings extends Extension public function onBulkExport(BulkExportEvent $event): void { - $event->fields["rating"] = $event->image->rating; + $event->fields["rating"] = $event->image['rating']; } public function onBulkImport(BulkImportEvent $event): void { @@ -198,10 +198,10 @@ class Ratings extends Extension public function onRatingSet(RatingSetEvent $event): void { - if (empty($event->image->rating)) { + if (empty($event->image['rating'])) { $old_rating = ""; } else { - $old_rating = $event->image->rating; + $old_rating = $event->image['rating']; } $this->set_rating($event->image->id, $event->rating, $old_rating); } @@ -212,7 +212,7 @@ class Ratings extends Extension $event->add_part( $this->theme->get_rater_html( $event->image->id, - $event->image->rating, + $event->image['rating'], $user->can(Permissions::EDIT_IMAGE_RATING) ), 80 @@ -232,8 +232,8 @@ class Ratings extends Extension public function onParseLinkTemplate(ParseLinkTemplateEvent $event): void { - if(!is_null($event->image->rating)) { - $event->replace('$rating', $this->rating_to_human($event->image->rating)); + if(!is_null($event->image['rating'])) { + $event->replace('$rating', $this->rating_to_human($event->image['rating'])); } } diff --git a/ext/relationships/main.php b/ext/relationships/main.php index e21a28d9..211b90f1 100644 --- a/ext/relationships/main.php +++ b/ext/relationships/main.php @@ -136,12 +136,12 @@ class Relationships extends Extension { global $database; - if (bool_escape($event->image->has_children)) { + if (bool_escape($event->image['has_children'])) { $database->execute("UPDATE images SET parent_id = NULL WHERE parent_id = :iid", ["iid" => $event->image->id]); } - if ($event->image->parent_id !== null) { - $this->set_has_children($event->image->parent_id); + if ($event->image['parent_id'] !== null) { + $this->set_has_children($event->image['parent_id']); } } diff --git a/ext/relationships/test.php b/ext/relationships/test.php index 6e310817..068b9bc6 100644 --- a/ext/relationships/test.php +++ b/ext/relationships/test.php @@ -24,12 +24,12 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_2 = Image::by_id($image_id_2); $image_3 = Image::by_id($image_id_3); - $this->assertNull($image_1->parent_id); - $this->assertNull($image_2->parent_id); - $this->assertNull($image_3->parent_id); - $this->assertFalse($image_1->has_children); - $this->assertFalse($image_2->has_children); - $this->assertFalse($image_3->has_children); + $this->assertNull($image_1['parent_id']); + $this->assertNull($image_2['parent_id']); + $this->assertNull($image_3['parent_id']); + $this->assertFalse($image_1['has_children']); + $this->assertFalse($image_2['has_children']); + $this->assertFalse($image_3['has_children']); return [$image_1, $image_2, $image_3]; } @@ -46,12 +46,12 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_2 = Image::by_id($image_2->id); $image_3 = Image::by_id($image_3->id); - $this->assertNull($image_1->parent_id); - $this->assertEquals($image_1->id, $image_2->parent_id); - $this->assertNull($image_3->parent_id); - $this->assertTrue($image_1->has_children); - $this->assertFalse($image_2->has_children); - $this->assertFalse($image_3->has_children); + $this->assertNull($image_1['parent_id']); + $this->assertEquals($image_1->id, $image_2['parent_id']); + $this->assertNull($image_3['parent_id']); + $this->assertTrue($image_1['has_children']); + $this->assertFalse($image_2['has_children']); + $this->assertFalse($image_3['has_children']); return [$image_1, $image_2, $image_3]; } @@ -67,12 +67,12 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_2 = Image::by_id($image_2->id); $image_3 = Image::by_id($image_3->id); - $this->assertNull($image_1->parent_id); - $this->assertEquals($image_3->id, $image_2->parent_id); - $this->assertNull($image_3->parent_id); - $this->assertFalse($image_2->has_children); - $this->assertFalse($image_2->has_children); - $this->assertTrue($image_3->has_children); + $this->assertNull($image_1['parent_id']); + $this->assertEquals($image_3->id, $image_2['parent_id']); + $this->assertNull($image_3['parent_id']); + $this->assertFalse($image_2['has_children']); + $this->assertFalse($image_2['has_children']); + $this->assertTrue($image_3['has_children']); return [$image_1, $image_2, $image_3]; } @@ -107,12 +107,12 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_2 = Image::by_id($image_2->id); $image_3 = Image::by_id($image_3->id); - $this->assertNull($image_1->parent_id); - $this->assertNull($image_2->parent_id); - $this->assertNull($image_3->parent_id); - $this->assertFalse($image_2->has_children); - $this->assertFalse($image_2->has_children); - $this->assertFalse($image_3->has_children); + $this->assertNull($image_1['parent_id']); + $this->assertNull($image_2['parent_id']); + $this->assertNull($image_3['parent_id']); + $this->assertFalse($image_2['has_children']); + $this->assertFalse($image_2['has_children']); + $this->assertFalse($image_3['has_children']); } //================================================================= @@ -130,12 +130,12 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_2 = Image::by_id($image_id_2); $image_3 = Image::by_id($image_id_3); - $this->assertNull($image_1->parent_id); - $this->assertNull($image_2->parent_id); - $this->assertNull($image_3->parent_id); - $this->assertFalse($image_1->has_children); - $this->assertFalse($image_2->has_children); - $this->assertFalse($image_3->has_children); + $this->assertNull($image_1['parent_id']); + $this->assertNull($image_2['parent_id']); + $this->assertNull($image_3['parent_id']); + $this->assertFalse($image_1['has_children']); + $this->assertFalse($image_2['has_children']); + $this->assertFalse($image_3['has_children']); return [$image_1, $image_2, $image_3]; } @@ -153,12 +153,12 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_3 = Image::by_id($image_3->id); $this->assertEquals(["pbx"], $image_2->get_tag_array()); - $this->assertNull($image_1->parent_id); - $this->assertEquals($image_1->id, $image_2->parent_id); - $this->assertNull($image_3->parent_id); - $this->assertTrue($image_1->has_children); - $this->assertFalse($image_2->has_children); - $this->assertFalse($image_3->has_children); + $this->assertNull($image_1['parent_id']); + $this->assertEquals($image_1->id, $image_2['parent_id']); + $this->assertNull($image_3['parent_id']); + $this->assertTrue($image_1['has_children']); + $this->assertFalse($image_2['has_children']); + $this->assertFalse($image_3['has_children']); return [$image_1, $image_2, $image_3]; } @@ -176,12 +176,12 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase $image_3 = Image::by_id($image_3->id); $this->assertEquals(["pbx"], $image_3->get_tag_array()); - $this->assertEquals($image_3->id, $image_1->parent_id); - $this->assertEquals($image_1->id, $image_2->parent_id); - $this->assertNull($image_3->parent_id); - $this->assertTrue($image_1->has_children); - $this->assertFalse($image_2->has_children); - $this->assertTrue($image_3->has_children); + $this->assertEquals($image_3->id, $image_1['parent_id']); + $this->assertEquals($image_1->id, $image_2['parent_id']); + $this->assertNull($image_3['parent_id']); + $this->assertTrue($image_1['has_children']); + $this->assertFalse($image_2['has_children']); + $this->assertTrue($image_3['has_children']); return [$image_1, $image_2, $image_3]; } @@ -193,7 +193,7 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase assert(!is_null($image_3)); // check parent is set - $this->assertEquals($image_2->parent_id, $image_1->id); + $this->assertEquals($image_2['parent_id'], $image_1->id); // un-set it send_event(new TagSetEvent($image_2, ["pbx", "parent:none"])); @@ -203,6 +203,6 @@ class RelationshipsTest extends ShimmiePHPUnitTestCase // check it was unset $this->assertEquals(["pbx"], $image_2->get_tag_array()); - $this->assertNull($image_2->parent_id); + $this->assertNull($image_2['parent_id']); } } diff --git a/ext/relationships/theme.php b/ext/relationships/theme.php index 9b976a32..43bf59e9 100644 --- a/ext/relationships/theme.php +++ b/ext/relationships/theme.php @@ -14,12 +14,12 @@ class RelationshipsTheme extends Themelet { global $page, $database; - if ($image->parent_id !== null) { - $a = "parent post"; + if ($image['parent_id'] !== null) { + $a = "parent post"; $page->add_block(new Block(null, "This post belongs to a $a.", "main", 5, "ImageHasParent")); } - if (bool_escape($image->has_children)) { + if (bool_escape($image['has_children'])) { $ids = $database->get_col("SELECT id FROM images WHERE parent_id = :iid", ["iid" => $image->id]); $html = "This post has ".(count($ids) > 1 ? "child posts" : "a child post").""; @@ -39,8 +39,8 @@ class RelationshipsTheme extends Themelet return SHM_POST_INFO( "Parent", - strval($image->parent_id) ?: "None", - !$user->is_anonymous() ? INPUT(["type" => "number", "name" => "tag_edit__parent", "value" => $image->parent_id]) : null + strval($image['parent_id']) ?: "None", + !$user->is_anonymous() ? INPUT(["type" => "number", "name" => "tag_edit__parent", "value" => $image['parent_id']]) : null ); } diff --git a/ext/trash/main.php b/ext/trash/main.php index c63b2897..0f204377 100644 --- a/ext/trash/main.php +++ b/ext/trash/main.php @@ -49,7 +49,7 @@ class Trash extends Extension { global $user; - if ($image->trash === true && !$user->can(Permissions::VIEW_TRASH)) { + if ($image['trash'] === true && !$user->can(Permissions::VIEW_TRASH)) { return false; } return true; @@ -77,7 +77,7 @@ class Trash extends Extension public function onImageDeletion(ImageDeletionEvent $event): void { - if ($event->force !== true && $event->image->trash !== true) { + if ($event->force !== true && $event->image['trash'] !== true) { self::set_trash($event->image->id, true); $event->stop_processing = true; } @@ -157,7 +157,7 @@ class Trash extends Extension public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event): void { global $user; - if ($event->image->trash === true && $user->can(Permissions::VIEW_TRASH)) { + if ($event->image['trash'] === true && $user->can(Permissions::VIEW_TRASH)) { $event->add_part($this->theme->get_image_admin_html($event->image->id)); } } diff --git a/tests/phpstan.neon b/tests/phpstan.neon index 0bea408a..a8e1f56e 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -6,8 +6,6 @@ parameters: - ../ext - ../tests - ../themes - ignoreErrors: - - '#Access to an undefined property Shimmie2\\Image::\$#' dynamicConstantNames: - DEBUG - SPEED_HAX diff --git a/themes/danbooru/view.theme.php b/themes/danbooru/view.theme.php index 1fc77387..43c4332f 100644 --- a/themes/danbooru/view.theme.php +++ b/themes/danbooru/view.theme.php @@ -51,10 +51,10 @@ class CustomViewPostTheme extends ViewPostTheme } if (Extension::is_enabled(RatingsInfo::KEY)) { - if ($image->rating === null || $image->rating == "?") { - $image->rating = "?"; + if ($image['rating'] === null || $image['rating'] == "?") { + $image['rating'] = "?"; } - $h_rating = Ratings::rating_to_human($image->rating); + $h_rating = Ratings::rating_to_human($image['rating']); $html .= "
Rating: $h_rating"; } diff --git a/themes/danbooru2/view.theme.php b/themes/danbooru2/view.theme.php index 3141f41e..e795b42f 100644 --- a/themes/danbooru2/view.theme.php +++ b/themes/danbooru2/view.theme.php @@ -52,12 +52,12 @@ class CustomViewPostTheme extends ViewPostTheme } if (Extension::is_enabled(RatingsInfo::KEY)) { - if ($image->rating === null || $image->rating == "?") { - $image->rating = "?"; + if ($image['rating'] === null || $image['rating'] == "?") { + $image['rating'] = "?"; } // @phpstan-ignore-next-line - ??? if (Extension::is_enabled(RatingsInfo::KEY)) { - $h_rating = Ratings::rating_to_human($image->rating); + $h_rating = Ratings::rating_to_human($image['rating']); $html .= "
Rating: $h_rating"; } } diff --git a/themes/lite/view.theme.php b/themes/lite/view.theme.php index b887ab7d..aecae4dd 100644 --- a/themes/lite/view.theme.php +++ b/themes/lite/view.theme.php @@ -56,10 +56,10 @@ class CustomViewPostTheme extends ViewPostTheme } if (Extension::is_enabled(RatingsInfo::KEY)) { - if ($image->rating === null || $image->rating == "?") { - $image->rating = "?"; + if ($image['rating'] === null || $image['rating'] == "?") { + $image['rating'] = "?"; } - $h_rating = Ratings::rating_to_human($image->rating); + $h_rating = Ratings::rating_to_human($image['rating']); $html .= "
Rating: $h_rating"; }