Skip to content

Monthly Shift: July 2026 - #503

Open
kantorge wants to merge 5 commits into
developfrom
shift-2026-07
Open

Monthly Shift: July 2026#503
kantorge wants to merge 5 commits into
developfrom
shift-2026-07

Conversation

@kantorge

@kantorge kantorge commented Jul 7, 2026

Copy link
Copy Markdown
Owner

This is an automated pull request included with your Shifty Plan. It contains curated refactors to keep your Laravel application aligned with the latest conventions and features.

This month focuses on a set of curated refactors to keep your Laravel apps modernized and your code streamlined. These are a subset of the refactors performed by the Laravel Fixer, which is included with your subscription.

Before merging, you should:

  • Checkout the shift-2026-07 branch
  • Review all pull request comments for additional changes
  • Test your application

If you do not wish to adopt these refactors, you may simply close this pull request and delete its branch.

Summary by CodeRabbit

  • Bug Fixes

    • Improved date handling across pricing, scheduling, reporting, and transaction features for more consistent behavior.
    • Tightened validation in several areas, reducing the chance of invalid requests or malformed data slipping through.
  • Refactor

    • Streamlined various checks and error conditions for clearer, more reliable processing.
    • Standardized internal date and logging usage across the app and tests.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR performs a codebase-wide dependency normalization: replacing Carbon\Carbon imports with Illuminate\Support\Carbon, and unqualified DB/Log imports with their Illuminate facade equivalents, across app and test files. It also refactors numerous explicit if/throw/abort validation blocks into Laravel's throw_if, throw_unless, abort_if, and abort_unless helpers, preserving existing exception types, messages, and conditions. One minor query change removes an explicit asc ordering direction, relying on the default.

Changes

Import normalization and guard-clause refactor

Layer / File(s) Summary
Carbon import switch in contracts, models, traits
app/Contracts/InvestmentPriceProvider.php, app/Http/Traits/CurrencyTrait.php, app/Http/Traits/ScheduleTrait.php, app/Models/Account.php, app/Models/AccountGroup.php, app/Models/AccountMonthlySummary.php, app/Models/Category.php, app/Models/Currency.php, app/Models/Transaction.php
Carbon import switched from Carbon\Carbon to Illuminate\Support\Carbon; PHPDoc annotations updated accordingly.
ScheduleTrait and Currency validation guard rewrite
app/Http/Traits/ScheduleTrait.php, app/Models/Currency.php
if/throw blocks for startType/customStart and currency-rate validations rewritten as throw_if/throw_unless calls with equivalent conditions.
Carbon/Log import switch in controllers, commands, jobs, listeners, services
app/Console/Commands/*, app/Http/Controllers/API/*, app/Jobs/*, app/Listeners/*, app/Services/CurrencyRateService.php, app/Services/DuplicateDetectionService.php, app/Services/InvestmentService.php, app/Services/InvestmentPriceProviders/*
Carbon\Carbon and unqualified Log imports replaced with Illuminate\Support\Carbon / Illuminate\Support\Facades\Log across many files; one query removes explicit asc ordering.
Controller authorization/validation guard rewrite
app/Http/Controllers/API/AccountApiController.php, app/Http/Controllers/API/CategoryLearningApiController.php, app/Http/Controllers/AccountEntityController.php, app/Http/Controllers/AiDocumentController.php, app/Http/Controllers/TransactionController.php
Ownership/type validation if/abort blocks converted to throw_if/abort_if/abort_unless calls preserving thrown exceptions and HTTP status codes.
AI pipeline service guard rewrite
app/Services/AiStepGateway.php, app/Services/ProcessDocumentService.php, app/Services/TextExtractionService.php, app/Services/OcrService.php, app/Services/ImagePreprocessingService.php
Payload/content/process validations converted from if blocks to throw_unless/throw_if calls with unchanged exception types.
Investment price provider validation guard rewrite
app/Services/InvestmentPriceProviderContextResolver.php, app/Services/InvestmentPriceProviderRegistry.php, app/Services/InvestmentPriceProviders/AlphaVantageProvider.php, .../GenericApiProvider.php, .../WebScrapingProvider.php, app/Services/TransactionItemMergeService.php
Credential, endpoint, HTTP method, response-payload, and amount-mismatch validations rewritten using throw_if/throw_unless.
Migration validation guard rewrite
database/migrations/2026_01_31_000001_...php, 2026_01_31_000002_...php, 2026_04_02_173148_...php
Legacy-value, negative-value, and JSON encoding failure checks converted from if/throw to throw_if.
Test suite import and guard updates
tests/Browser/*, tests/Feature/*, tests/Unit/*
Test Carbon imports switched to Illuminate\Support\Carbon, DB imports switched to Illuminate\Support\Facades\DB, helper parameter types updated, and one mocked failure conditional converted to throw_if.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

  • kantorge/yaffa#448: Overlaps directly with edits to AiStepGateway and ProcessDocumentService guard/validation logic.
  • kantorge/yaffa#478: Intersects with the CurrencyTrait.php Carbon import change touched in both PRs.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and does not describe the main refactor, only the month-based shift label. Use a specific title like 'Refactor Laravel imports and guard clauses for July 2026' to reflect the actual changes.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch shift-2026-07

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the codebase by standardizing Carbon imports to Illuminate\Support\Carbon, cleaning up facade imports, and replacing standard conditional exception/abort blocks with Laravel's throw_if, throw_unless, abort_if, and abort_unless helpers. However, the widespread use of throw_if and throw_unless with eagerly instantiated exception objects (and associated string formatting) introduces noticeable performance overhead and unnecessary memory allocation on successful execution paths. It is highly recommended to either revert these to standard if statements or pass the exception class name and constructor arguments to the helpers to enable lazy instantiation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +98 to +105
throw_if(abs($originalTotal - $newTotal) > self::AMOUNT_COMPARISON_EPSILON, new RuntimeException(
sprintf(
'Transaction item merge aborted: amount mismatch for transaction %d (original %.4f vs new %.4f).',
$transaction->id,
$originalTotal,
$newTotal,
)
));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using throw_if here introduces a significant performance regression. In PHP, arguments to functions are evaluated eagerly. This means sprintf() is executed and a new RuntimeException object (including its expensive stack trace generation) is instantiated on every single successful merge operation, even when there is no amount mismatch.

It is highly recommended to revert this change to the original if statement so that the string formatting and exception instantiation only occur in the exceptional case when a mismatch actually happens.

        if (abs($originalTotal - $newTotal) > self::AMOUNT_COMPARISON_EPSILON) {
            throw new RuntimeException(
                sprintf(
                    'Transaction item merge aborted: amount mismatch for transaction %d (original %.4f vs new %.4f).',
                    $transaction->id,
                    $originalTotal,
                    $newTotal,
                )
            );
        }

Comment on lines +52 to +62
throw_if(isset($data->{'Error Message'}), new InvalidPriceDataException(
"Alpha Vantage API error: {$data->{'Error Message'}}",
'alpha_vantage',
$investment->symbol
));

if (isset($data->Note)) {
throw new PriceProviderException(
"Alpha Vantage rate limit: {$data->Note}",
'alpha_vantage',
$investment->symbol
);
}
throw_if(isset($data->Note), new PriceProviderException(
"Alpha Vantage rate limit: {$data->Note}",
'alpha_vantage',
$investment->symbol
));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Inside this block, if Time Series (Daily) is missing but neither Error Message nor Note is present in the response, the code will eagerly instantiate both InvalidPriceDataException and PriceProviderException before finally throwing the fallback InvalidPriceDataException on line 64.

Using throw_if here is highly inefficient and confusing. It is much cleaner and more performant to use standard if statements or to pass the exception class names and arguments to throw_if to avoid eager instantiation. Reverting to standard if statements is recommended here for readability and performance.

                if (isset($data->{'Error Message'})) {
                    throw new InvalidPriceDataException(
                        "Alpha Vantage API error: {$data->{'Error Message'}}",
                        'alpha_vantage',
                        $investment->symbol
                    );
                }

                if (isset($data->Note)) {
                    throw new PriceProviderException(
                        "Alpha Vantage rate limit: {$data->Note}",
                        'alpha_vantage',
                        $investment->symbol
                    );
                }

if ($accountEntity !== null && $accountEntity->user_id !== $user->id) {
throw new AuthorizationException('You do not have permission to access this account entity.');
}
throw_if($accountEntity !== null && $accountEntity->user_id !== $user->id, new AuthorizationException('You do not have permission to access this account entity.'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using throw_if with an instantiated exception object (new AuthorizationException(...)) causes the exception to be eagerly instantiated on every single request, even when the user is fully authorized. Generating exception stack traces is a relatively expensive operation in PHP.

To prevent this performance overhead, you can pass the exception class name as a string and the message as a subsequent argument. The throw_if helper will then lazily instantiate the exception only when the condition is met.

        throw_if($accountEntity !== null && $accountEntity->user_id !== $user->id, AuthorizationException::class, 'You do not have permission to access this account entity.');

Comment on lines +33 to +43
throw_if(! is_string($providerKey) || $providerKey === '', new PriceProviderException(
'Investment has no price provider configured',
'none',
$investment->symbol
));

throw_unless($this->providerRegistry->has($providerKey), new PriceProviderException(
"Investment has unknown provider: {$providerKey}",
$providerKey,
$investment->symbol
));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using throw_if and throw_unless with eagerly instantiated exception objects (new PriceProviderException(...)) causes these exceptions to be created on every single resolution call, even when the provider key is valid and registered.

To avoid this overhead, pass the exception class name as a string and the constructor arguments as subsequent parameters so that the exception is only instantiated when the condition is met.

        throw_if(! is_string($providerKey) || $providerKey === '', PriceProviderException::class, 'Investment has no price provider configured', 'none', $investment->symbol);

        throw_unless($this->providerRegistry->has($providerKey), PriceProviderException::class, "Investment has unknown provider: {$providerKey}", $providerKey, $investment->symbol);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
app/Services/InvestmentPriceProviders/AlphaVantageProvider.php (1)

148-156: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Same eager-exception-construction issue as fetchPrices().

throw_if(isset($data->{'Error Message'}), new PriceProviderException("...{$data->{'Error Message'}}"...)) and the Note guard below it unconditionally construct the exception (and interpolate the property) even when the property is absent, emitting undefined-property warnings on the normal/success path. Same fix as above: revert these two guards to if (isset(...)) { throw ... }.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Services/InvestmentPriceProviders/AlphaVantageProvider.php` around lines
148 - 156, The two Alpha Vantage error guards in AlphaVantageProvider are
eagerly constructing PriceProviderException and interpolating properties even on
the success path, which can trigger undefined-property warnings. Update the
error-handling block in the provider to use explicit if (isset(...)) checks for
both the "Error Message" and Note cases, and only instantiate and throw the
exception inside those branches, matching the safer pattern used in
fetchPrices().
🧹 Nitpick comments (2)
database/migrations/2026_04_02_173148_backfill_web_scraping_provider_settings_on_investments_table.php (1)

45-50: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Minor: exception object built on every iteration, even on success.

throw_if($encodedSettings === false, new RuntimeException(sprintf(...))) evaluates the sprintf and json_last_error_msg() call on every changed row regardless of whether encoding actually failed, since the exception argument is constructed eagerly before throw_if checks the condition. Impact is negligible here (bounded chunk size, one-time migration), but worth noting for readers unfamiliar with the eager-evaluation gotcha of throw_if/throw_unless helpers.

♻️ Optional refactor to avoid eager construction
-                    $encodedSettings = json_encode($settings);
-                    throw_if($encodedSettings === false, new RuntimeException(sprintf(
-                        'Failed to encode provider_settings for investment id %d: %s',
-                        (int) $investment->id,
-                        json_last_error_msg()
-                    )));
+                    $encodedSettings = json_encode($settings);
+
+                    if ($encodedSettings === false) {
+                        throw new RuntimeException(sprintf(
+                            'Failed to encode provider_settings for investment id %d: %s',
+                            (int) $investment->id,
+                            json_last_error_msg()
+                        ));
+                    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@database/migrations/2026_04_02_173148_backfill_web_scraping_provider_settings_on_investments_table.php`
around lines 45 - 50, The migration’s provider_settings encoding check eagerly
builds the RuntimeException for every investment even when json_encode succeeds.
Update the logic in the backfill migration’s encoding block to only construct
the exception inside the failure path (for example, an explicit conditional
around the json_encode result) so the expensive sprintf/json_last_error_msg work
is deferred until needed.
app/Http/Traits/ScheduleTrait.php (1)

29-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use word-style logical operators per project convention.

Line 31 uses symbolic &&/! instead of the project's preferred and/not. Logic itself is correct and matches existing call sites.

As per coding guidelines, "Use logical operators (and, or, not) instead of symbolic operators (&&, ||, !) in PHP control structures."

✏️ Suggested style fix
-        throw_if($startType === 'custom' && ! $customStart, new InvalidArgumentException('Custom start date is required for custom start type'));
+        throw_if($startType === 'custom' and not $customStart, new InvalidArgumentException('Custom start date is required for custom start type'));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Http/Traits/ScheduleTrait.php` around lines 29 - 31, The conditional in
ScheduleTrait’s throw_if check uses symbolic operators instead of the project’s
preferred word-style logical operators. Update the custom start validation in
ScheduleTrait so the existing logic is expressed with and/not rather than &&/!,
keeping the same throw_if behavior and InvalidArgumentException message.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Services/InvestmentPriceProviders/AlphaVantageProvider.php`:
- Around line 52-62: Replace the two `throw_if()` calls in
`AlphaVantageProvider::getPriceData` with explicit `if` guards so the exception
objects are only constructed when the response actually contains `Error Message`
or `Note`. Access `{$data->{'Error Message'}}` and `{$data->Note}` only inside
those conditionals, and throw `InvalidPriceDataException` or
`PriceProviderException` directly from the guarded blocks to avoid
undefined-property warnings and unnecessary exception creation.

In `@app/Services/OcrService.php`:
- Line 124: The OCR process failure check in OcrService::run should keep the
explicit success check and throw path instead of using throw_unless, because the
ProcessFailedException is being constructed eagerly and can fail even when the
process succeeds. Replace the throw_unless usage with a normal conditional
around the $process->isSuccessful() check, and only instantiate
ProcessFailedException with $process when the process is actually unsuccessful.

---

Duplicate comments:
In `@app/Services/InvestmentPriceProviders/AlphaVantageProvider.php`:
- Around line 148-156: The two Alpha Vantage error guards in
AlphaVantageProvider are eagerly constructing PriceProviderException and
interpolating properties even on the success path, which can trigger
undefined-property warnings. Update the error-handling block in the provider to
use explicit if (isset(...)) checks for both the "Error Message" and Note cases,
and only instantiate and throw the exception inside those branches, matching the
safer pattern used in fetchPrices().

---

Nitpick comments:
In `@app/Http/Traits/ScheduleTrait.php`:
- Around line 29-31: The conditional in ScheduleTrait’s throw_if check uses
symbolic operators instead of the project’s preferred word-style logical
operators. Update the custom start validation in ScheduleTrait so the existing
logic is expressed with and/not rather than &&/!, keeping the same throw_if
behavior and InvalidArgumentException message.

In
`@database/migrations/2026_04_02_173148_backfill_web_scraping_provider_settings_on_investments_table.php`:
- Around line 45-50: The migration’s provider_settings encoding check eagerly
builds the RuntimeException for every investment even when json_encode succeeds.
Update the logic in the backfill migration’s encoding block to only construct
the exception inside the failure path (for example, an explicit conditional
around the json_encode result) so the expensive sprintf/json_last_error_msg work
is deferred until needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7fbcd371-d022-404f-b1b2-85a519166f59

📥 Commits

Reviewing files that changed from the base of the PR and between d5345be and a16ab49.

📒 Files selected for processing (70)
  • app/Console/Commands/GetInvestmentPrices.php
  • app/Console/Commands/ProcessAiDocuments.php
  • app/Console/Commands/RecordScheduledTransactions.php
  • app/Console/Commands/ResetDemoDatabase.php
  • app/Contracts/InvestmentPriceProvider.php
  • app/Http/Controllers/API/AccountApiController.php
  • app/Http/Controllers/API/AiDocumentApiController.php
  • app/Http/Controllers/API/AiProviderConfigApiController.php
  • app/Http/Controllers/API/CategoryLearningApiController.php
  • app/Http/Controllers/API/InvestmentApiController.php
  • app/Http/Controllers/API/InvestmentPriceApiController.php
  • app/Http/Controllers/API/InvestmentPriceProviderApiController.php
  • app/Http/Controllers/API/ReportApiController.php
  • app/Http/Controllers/AccountEntityController.php
  • app/Http/Controllers/AiDocumentController.php
  • app/Http/Controllers/TransactionController.php
  • app/Http/Traits/CurrencyTrait.php
  • app/Http/Traits/ScheduleTrait.php
  • app/Jobs/AiProcessingJob.php
  • app/Jobs/CalculateAccountMonthlySummary.php
  • app/Listeners/CreateAiDocumentFromSource.php
  • app/Listeners/ProcessTransactionUpdated.php
  • app/Models/Account.php
  • app/Models/AccountGroup.php
  • app/Models/AccountMonthlySummary.php
  • app/Models/Category.php
  • app/Models/Currency.php
  • app/Models/Transaction.php
  • app/Services/AiStepGateway.php
  • app/Services/CurrencyRateService.php
  • app/Services/DuplicateDetectionService.php
  • app/Services/ImagePreprocessingService.php
  • app/Services/InvestmentPriceProviderContextResolver.php
  • app/Services/InvestmentPriceProviderRegistry.php
  • app/Services/InvestmentPriceProviders/AlphaVantageProvider.php
  • app/Services/InvestmentPriceProviders/GenericApiProvider.php
  • app/Services/InvestmentPriceProviders/WebScrapingProvider.php
  • app/Services/InvestmentService.php
  • app/Services/OcrService.php
  • app/Services/ProcessDocumentService.php
  • app/Services/TextExtractionService.php
  • app/Services/TransactionItemMergeService.php
  • database/migrations/2026_01_31_000001_add_transaction_type_enum_column_to_transactions_table.php
  • database/migrations/2026_01_31_000002_add_unsigned_to_decimal_columns.php
  • database/migrations/2026_04_02_173148_backfill_web_scraping_provider_settings_on_investments_table.php
  • tests/Browser/Pages/Transactions/TransactionShowInvestmentModalTest.php
  • tests/Feature/API/V1/InvestmentPriceProviderApiV1Test.php
  • tests/Feature/AiProviderConfigApiControllerTest.php
  • tests/Feature/CategoryTest.php
  • tests/Feature/Console/GetInvestmentPricesCommandTest.php
  • tests/Feature/Console/RecordScheduledTransactionsCommandTest.php
  • tests/Feature/CurrencyRateTest.php
  • tests/Feature/GoogleDriveConfigApiControllerTest.php
  • tests/Feature/PayeeStatsApiControllerTest.php
  • tests/Feature/ProcessGoogleDriveConfigJobTest.php
  • tests/Unit/Console/Commands/RecordScheduledTransactionsTest.php
  • tests/Unit/Http/Controllers/API/PayeeApiControllerTest.php
  • tests/Unit/Http/Controllers/API/PayeeStatsApiControllerTest.php
  • tests/Unit/Jobs/CalculateAccountMonthlySummaryTest.php
  • tests/Unit/Models/AiProviderConfigTest.php
  • tests/Unit/Models/CurrencyTest.php
  • tests/Unit/Models/GoogleDriveConfigTest.php
  • tests/Unit/Models/InvestmentProviderConfigTest.php
  • tests/Unit/Models/TransactionScheduleTest.php
  • tests/Unit/Services/DuplicateDetectionServiceTest.php
  • tests/Unit/Services/InvestmentPriceProviders/AlphaVantageProviderTest.php
  • tests/Unit/Services/InvestmentPriceProviders/GenericApiProviderTest.php
  • tests/Unit/Services/InvestmentPriceProviders/WebScrapingProviderTest.php
  • tests/Unit/Services/InvestmentServicePriceTest.php
  • tests/Unit/Services/ProcessDocumentServiceTest.php

Comment on lines +52 to +62
throw_if(isset($data->{'Error Message'}), new InvalidPriceDataException(
"Alpha Vantage API error: {$data->{'Error Message'}}",
'alpha_vantage',
$investment->symbol
));

if (isset($data->Note)) {
throw new PriceProviderException(
"Alpha Vantage rate limit: {$data->Note}",
'alpha_vantage',
$investment->symbol
);
}
throw_if(isset($data->Note), new PriceProviderException(
"Alpha Vantage rate limit: {$data->Note}",
'alpha_vantage',
$investment->symbol
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== AlphaVantageProvider excerpt ==\n'
sed -n '1,140p' app/Services/InvestmentPriceProviders/AlphaVantageProvider.php | cat -n

printf '\n== throw_if definition search ==\n'
rg -n "function throw_if|throw_if\(" -S app vendor bootstrap config --glob '!vendor/composer/*' || true

printf '\n== PHP eager evaluation probe ==\n'
php -d display_errors=1 -d error_reporting=E_ALL <<'PHP'
<?php
class E extends Exception {}

$obj = (object) [];
function probe($cond, $ex) {
    echo "probe called\n";
}

echo "1) direct interpolation in arguments\n";
try {
    probe(isset($obj->{'Error Message'}), new E("msg: {$obj->{'Error Message'}}"));
} catch (Throwable $t) {
    echo get_class($t), ": ", $t->getMessage(), "\n";
}

echo "2) guarded if\n";
try {
    if (isset($obj->{'Error Message'})) {
        throw new E("msg: {$obj->{'Error Message'}}");
    }
    echo "no warning\n";
} catch (Throwable $t) {
    echo get_class($t), ": ", $t->getMessage(), "\n";
}
PHP

Repository: kantorge/yaffa

Length of output: 13360


Use if guards here instead of throw_if with prebuilt exceptions. The exception arguments are evaluated before throw_if() runs, so {$data->{'Error Message'}} and {$data->Note} are accessed even when those fields are missing, which raises undefined-property warnings on the common “missing Time Series” path. Reverting these two branches to conditional throws avoids the warning and the extra exception construction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Services/InvestmentPriceProviders/AlphaVantageProvider.php` around lines
52 - 62, Replace the two `throw_if()` calls in
`AlphaVantageProvider::getPriceData` with explicit `if` guards so the exception
objects are only constructed when the response actually contains `Error Message`
or `Note`. Access `{$data->{'Error Message'}}` and `{$data->Note}` only inside
those conditionals, and throw `InvalidPriceDataException` or
`PriceProviderException` directly from the guarded blocks to avoid
undefined-property warnings and unnecessary exception creation.

if (! $process->isSuccessful()) {
throw new ProcessFailedException($process);
}
throw_unless($process->isSuccessful(), new ProcessFailedException($process));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Keep the explicit if/throw here instead of throw_unless. new ProcessFailedException($process) is evaluated eagerly and can throw for a successful process.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Services/OcrService.php` at line 124, The OCR process failure check in
OcrService::run should keep the explicit success check and throw path instead of
using throw_unless, because the ProcessFailedException is being constructed
eagerly and can fail even when the process succeeds. Replace the throw_unless
usage with a normal conditional around the $process->isSuccessful() check, and
only instantiate ProcessFailedException with $process when the process is
actually unsuccessful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants